From 305c4ce882e08202b782f20200fa5d0ffd7ee8ed Mon Sep 17 00:00:00 2001 From: Kristof De Langhe Date: Mon, 24 Feb 2020 17:13:12 +0100 Subject: [PATCH] 68954: Display claimed task actions depending on config from REST API Conflicts: src/app/core/core.module.ts src/app/shared/mydspace-actions/claimed-task/claimed-task-actions.component.html --- src/app/core/core.module.ts | 4 + .../core/data/workflow-action-data.service.ts | 40 +++++++++ ...normalized-workflow-action-object.model.ts | 23 +++++ .../models/workflow-action-object.model.ts | 19 +++++ ...claimed-task-actions-abstract.component.ts | 28 ++++++ ...laimed-task-actions-approve.component.html | 8 +- .../claimed-task-actions-approve.component.ts | 41 ++++----- .../claimed-task-actions.component.html | 33 +++---- .../claimed-task-actions.component.ts | 62 ++++---------- ...-task-actions-edit-metadata.component.html | 7 ++ ...-task-actions-edit-metadata.component.scss | 0 ...sk-actions-edit-metadata.component.spec.ts | 65 ++++++++++++++ ...ed-task-actions-edit-metadata.component.ts | 20 +++++ ...claimed-task-actions-reject.component.html | 14 +-- .../claimed-task-actions-reject.component.ts | 45 +++++----- ...task-actions-return-to-pool.component.html | 8 +- ...d-task-actions-return-to-pool.component.ts | 43 +++++----- .../claimed-task-actions-decorator.ts | 23 +++++ ...claimed-task-actions-loader.component.html | 1 + .../claimed-task-actions-loader.component.ts | 85 +++++++++++++++++++ .../claimed-task-actions.directive.ts | 11 +++ src/app/shared/shared.module.ts | 14 ++- 22 files changed, 452 insertions(+), 142 deletions(-) create mode 100644 src/app/core/data/workflow-action-data.service.ts create mode 100644 src/app/core/tasks/models/normalized-workflow-action-object.model.ts create mode 100644 src/app/core/tasks/models/workflow-action-object.model.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts create mode 100644 src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index 82edaed96b..ffa2852ddc 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -142,6 +142,8 @@ import { PoolTask } from './tasks/models/pool-task-object.model'; import { TaskObject } from './tasks/models/task-object.model'; import { PoolTaskDataService } from './tasks/pool-task-data.service'; import { TaskResponseParsingService } from './tasks/task-response-parsing.service'; +import { WorkflowActionDataService } from './data/workflow-action-data.service'; +import { NormalizedWorkflowAction } from './tasks/models/normalized-workflow-action-object.model'; /** * When not in production, endpoint responses can be mocked for testing purposes @@ -257,6 +259,7 @@ const PROVIDERS = [ LookupRelationService, LicenseDataService, ItemTypeDataService, + WorkflowActionDataService, // register AuthInterceptor as HttpInterceptor { provide: HTTP_INTERCEPTORS, @@ -305,6 +308,7 @@ export const models = ItemType, ExternalSource, ExternalSourceEntry, + NormalizedWorkflowAction ]; @NgModule({ diff --git a/src/app/core/data/workflow-action-data.service.ts b/src/app/core/data/workflow-action-data.service.ts new file mode 100644 index 0000000000..17b861e1af --- /dev/null +++ b/src/app/core/data/workflow-action-data.service.ts @@ -0,0 +1,40 @@ +import { DataService } from './data.service'; +import { WorkflowAction } from '../tasks/models/workflow-action-object.model'; +import { RequestService } from './request.service'; +import { RemoteDataBuildService } from '../cache/builders/remote-data-build.service'; +import { NormalizedObjectBuildService } from '../cache/builders/normalized-object-build.service'; +import { Store } from '@ngrx/store'; +import { CoreState } from '../core.reducers'; +import { ObjectCacheService } from '../cache/object-cache.service'; +import { HALEndpointService } from '../shared/hal-endpoint.service'; +import { NotificationsService } from '../../shared/notifications/notifications.service'; +import { HttpClient } from '@angular/common/http'; +import { DefaultChangeAnalyzer } from './default-change-analyzer.service'; +import { FindListOptions } from './request.models'; +import { Observable } from 'rxjs/internal/Observable'; +import { Injectable } from '@angular/core'; + +/** + * A service responsible for fetching/sending data from/to the REST API on the workflowactions endpoint + */ +@Injectable() +export class WorkflowActionDataService extends DataService { + protected linkPath = 'workflowactions'; + + constructor( + protected requestService: RequestService, + protected rdbService: RemoteDataBuildService, + protected dataBuildService: NormalizedObjectBuildService, + protected store: Store, + protected objectCache: ObjectCacheService, + protected halService: HALEndpointService, + protected notificationsService: NotificationsService, + protected http: HttpClient, + protected comparator: DefaultChangeAnalyzer) { + super(); + } + + getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable { + return this.halService.getEndpoint(this.linkPath); + } +} diff --git a/src/app/core/tasks/models/normalized-workflow-action-object.model.ts b/src/app/core/tasks/models/normalized-workflow-action-object.model.ts new file mode 100644 index 0000000000..a5d1754a4c --- /dev/null +++ b/src/app/core/tasks/models/normalized-workflow-action-object.model.ts @@ -0,0 +1,23 @@ +import { autoserialize, inheritSerialization } from 'cerialize'; +import { mapsTo } from '../../cache/builders/build-decorators'; +import { WorkflowAction } from './workflow-action-object.model'; +import { NormalizedDSpaceObject } from '../../cache/models/normalized-dspace-object.model'; + +/** + * A normalized model class for a WorkflowAction + */ +@mapsTo(WorkflowAction) +@inheritSerialization(NormalizedDSpaceObject) +export class NormalizedWorkflowAction extends NormalizedDSpaceObject { + /** + * The workflow action's identifier + */ + @autoserialize + id: string; + + /** + * The options available for this workflow action + */ + @autoserialize + options: 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 new file mode 100644 index 0000000000..57d90354f0 --- /dev/null +++ b/src/app/core/tasks/models/workflow-action-object.model.ts @@ -0,0 +1,19 @@ +import { ResourceType } from '../../shared/resource-type'; +import { DSpaceObject } from '../../shared/dspace-object.model'; + +/** + * A model class for a WorkflowAction + */ +export class WorkflowAction extends DSpaceObject { + static type = new ResourceType('workflowaction'); + + /** + * The workflow action's identifier + */ + id: string; + + /** + * The options available for this workflow action + */ + options: string[]; +} diff --git a/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts b/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts new file mode 100644 index 0000000000..761f088389 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/abstract/claimed-task-actions-abstract.component.ts @@ -0,0 +1,28 @@ +import { EventEmitter, Input, Output } from '@angular/core'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; + +/** + * Abstract component for rendering a claimed task's action + */ +export abstract class ClaimedTaskActionsAbstractComponent { + /** + * The Claimed Task to display an action for + */ + @Input() object: ClaimedTask; + + /** + * Emits the success or failure of a processed action + */ + @Output() processCompleted: EventEmitter = new EventEmitter(); + + /** + * A boolean representing if the operation is pending + */ + processing$ = new BehaviorSubject(false); + + /** + * Method called when the action's button is clicked + */ + abstract process(); +} diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html index 3c41fdbb07..78e5673922 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.html @@ -1,8 +1,8 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts index 8e7c0dab60..13cc50b6c0 100644 --- a/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/approve/claimed-task-actions-approve.component.ts @@ -1,32 +1,33 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +@rendersWorkflowTaskOption('submit_approve') @Component({ selector: 'ds-claimed-task-actions-approve', styleUrls: ['./claimed-task-actions-approve.component.scss'], templateUrl: './claimed-task-actions-approve.component.html', }) +/** + * Component for displaying and processing the approve action on a workflow task item + */ +export class ClaimedTaskActionsApproveComponent extends ClaimedTaskActionsAbstractComponent { -export class ClaimedTaskActionsApproveComponent { + constructor(protected claimedTaskService: ClaimedTaskDataService) { + super(); + } /** - * A boolean representing if a reject operation is pending + * Approve the task */ - @Input() processingApprove: boolean; - - /** - * CSS classes to append to reject button - */ - @Input() wrapperClass: string; - - /** - * An event fired when a approve action is confirmed. - */ - @Output() approve: EventEmitter = new EventEmitter(); - - /** - * Emit approve event - */ - confirmApprove() { - this.approve.emit(); + process() { + this.processing$.next(true); + this.claimedTaskService.approveTask(this.object.id) + .subscribe((res: ProcessTaskResponse) => { + this.processing$.next(false); + this.processCompleted.emit(res.hasSucceeded); + }); } } 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 df8fb0eae7..aa569bbfc8 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 @@ -1,20 +1,13 @@ - - - {{'submission.workflow.tasks.claimed.edit' | 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 81d24fa1d7..c3802a3846 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 @@ -1,13 +1,12 @@ import { Component, Injector, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; -import { BehaviorSubject, Observable } from 'rxjs'; +import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; import { TranslateService } from '@ngx-translate/core'; import { ClaimedTaskDataService } from '../../../core/tasks/claimed-task-data.service'; import { ClaimedTask } from '../../../core/tasks/models/claimed-task-object.model'; -import { ProcessTaskResponse } from '../../../core/tasks/models/process-task-response'; import { isNotUndefined } from '../../empty.util'; import { WorkflowItem } from '../../../core/submission/models/workflowitem.model'; import { RemoteData } from '../../../core/data/remote-data'; @@ -15,6 +14,9 @@ import { MyDSpaceActionsComponent } from '../mydspace-actions'; import { NotificationsService } from '../../notifications/notifications.service'; import { RequestService } from '../../../core/data/request.service'; import { SearchService } from '../../../core/shared/search/search.service'; +import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model'; +import { WorkflowActionDataService } from '../../../core/data/workflow-action-data.service'; +import { WORKFLOW_TASK_OPTION_RETURN } from './return-to-pool/claimed-task-actions-return-to-pool.component'; /** * This component represents actions related to ClaimedTask object. @@ -37,19 +39,15 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent; /** - * A boolean representing if an approve operation is pending + * The workflow action available for this task */ - public processingApprove$ = new BehaviorSubject(false); + public actionRD$: Observable>; /** - * A boolean representing if a reject operation is pending + * The option used to render the "return to pool" component + * Every claimed task contains this option */ - public processingReject$ = new BehaviorSubject(false); - - /** - * A boolean representing if a return to pool operation is pending - */ - public processingReturnToPool$ = new BehaviorSubject(false); + public returnToPoolOption = WORKFLOW_TASK_OPTION_RETURN; /** * Initialize instance variables @@ -60,13 +58,15 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent { - this.processingApprove$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); - } - - /** - * Reject the task. - */ - reject(reason) { - this.processingReject$.next(true); - this.objectDataService.rejectTask(reason, this.object.id) - .subscribe((res: ProcessTaskResponse) => { - this.processingReject$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); - } - - /** - * Return task to the pool. - */ - returnToPool() { - this.processingReturnToPool$.next(true); - this.objectDataService.returnToPoolTask(this.object.id) - .subscribe((res: ProcessTaskResponse) => { - this.processingReturnToPool$.next(false); - this.handleActionResponse(res.hasSucceeded); - }); + initAction(object: ClaimedTask) { + this.actionRD$ = this.workflowActionService.findById(object.action); } } diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html new file mode 100644 index 0000000000..4a42378f7e --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.html @@ -0,0 +1,7 @@ + + {{'submission.workflow.tasks.claimed.edit' | translate}} + diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts new file mode 100644 index 0000000000..cd1f687544 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.spec.ts @@ -0,0 +1,65 @@ +import { ChangeDetectionStrategy, NO_ERRORS_SCHEMA } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; + +import { ClaimedTaskActionsEditMetadataComponent } from './claimed-task-actions-approve.component'; +import { MockTranslateLoader } from '../../../mocks/mock-translate-loader'; + +let component: ClaimedTaskActionsEditMetadataComponent; +let fixture: ComponentFixture; + +describe('ClaimedTaskActionsApproveComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: MockTranslateLoader + } + }) + ], + declarations: [ClaimedTaskActionsEditMetadataComponent], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(ClaimedTaskActionsEditMetadataComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ClaimedTaskActionsEditMetadataComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + afterEach(() => { + fixture = null; + component = null; + }); + + it('should display approve button', () => { + const btn = fixture.debugElement.query(By.css('.btn-success')); + + expect(btn).toBeDefined(); + }); + + it('should display spin icon when approve is pending', () => { + component.processingApprove = true; + fixture.detectChanges(); + + const span = fixture.debugElement.query(By.css('.btn-success .fa-spin')); + + expect(span).toBeDefined(); + }); + + it('should emit approve event', () => { + spyOn(component.approve, 'emit'); + + component.confirmApprove(); + fixture.detectChanges(); + + expect(component.approve.emit).toHaveBeenCalled(); + }); + +}); diff --git a/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts new file mode 100644 index 0000000000..c260459e70 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; + +@rendersWorkflowTaskOption('submit_edit_metadata') +@Component({ + selector: 'ds-claimed-task-actions-edit-metadata', + styleUrls: ['./claimed-task-actions-edit-metadata.component.scss'], + templateUrl: './claimed-task-actions-edit-metadata.component.html', +}) +/** + * Component for displaying the edit metadata action on a workflow task item + */ +export class ClaimedTaskActionsEditMetadataComponent extends ClaimedTaskActionsAbstractComponent { + process() { + // Nothing needs to happen for the edit-metadata button, it simply renders a link to another page + } +} diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html index 91edee66bd..975b964c1b 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.html @@ -1,10 +1,10 @@

@@ -21,17 +21,17 @@ -
+
diff --git a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts index b66c104695..05df9d6bf8 100644 --- a/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/reject/claimed-task-actions-reject.component.ts @@ -1,26 +1,22 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +@rendersWorkflowTaskOption('submit_reject') @Component({ selector: 'ds-claimed-task-actions-reject', styleUrls: ['./claimed-task-actions-reject.component.scss'], templateUrl: './claimed-task-actions-reject.component.html', }) - -export class ClaimedTaskActionsRejectComponent implements OnInit { - - /** - * A boolean representing if a reject operation is pending - */ - @Input() processingReject: boolean; - - /** - * CSS classes to append to reject button - */ - @Input() wrapperClass: string; - +/** + * Component for displaying and processing the reject action on a workflow task item + */ +export class ClaimedTaskActionsRejectComponent extends ClaimedTaskActionsAbstractComponent implements OnInit { /** * An event fired when a reject action is confirmed. * Event's payload equals to reject reason. @@ -42,8 +38,12 @@ export class ClaimedTaskActionsRejectComponent implements OnInit { * * @param {FormBuilder} formBuilder * @param {NgbModal} modalService + * @param claimedTaskService */ - constructor(private formBuilder: FormBuilder, private modalService: NgbModal) { + constructor(protected claimedTaskService: ClaimedTaskDataService, + private formBuilder: FormBuilder, + private modalService: NgbModal) { + super(); } /** @@ -53,17 +53,20 @@ export class ClaimedTaskActionsRejectComponent implements OnInit { this.rejectForm = this.formBuilder.group({ reason: ['', Validators.required] }); - } /** - * Close modal and emit reject event + * Reject the task */ - confirmReject() { - this.processingReject = true; - this.modalRef.close('Send Button'); + process() { + this.processing$.next(true); const reason = this.rejectForm.get('reason').value; - this.reject.emit(reason); + this.modalRef.close('Send Button'); + this.claimedTaskService.rejectTask(reason, this.object.id) + .subscribe((res: ProcessTaskResponse) => { + this.processing$.next(false); + this.processCompleted.emit(res.hasSucceeded); + }); } /** diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html index 702ce75e7f..67f1084eea 100644 --- a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.html @@ -1,8 +1,8 @@ diff --git a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts index 1dfe91eb5b..38f3ec4890 100644 --- a/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts +++ b/src/app/shared/mydspace-actions/claimed-task/return-to-pool/claimed-task-actions-return-to-pool.component.ts @@ -1,32 +1,35 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { Component } from '@angular/core'; +import { ProcessTaskResponse } from '../../../../core/tasks/models/process-task-response'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { rendersWorkflowTaskOption } from '../switcher/claimed-task-actions-decorator'; +import { ClaimedTaskDataService } from '../../../../core/tasks/claimed-task-data.service'; +export const WORKFLOW_TASK_OPTION_RETURN = 'return_to_pool'; + +@rendersWorkflowTaskOption(WORKFLOW_TASK_OPTION_RETURN) @Component({ selector: 'ds-claimed-task-actions-return-to-pool', styleUrls: ['./claimed-task-actions-return-to-pool.component.scss'], templateUrl: './claimed-task-actions-return-to-pool.component.html', }) +/** + * Component for displaying and processing the return to pool action on a workflow task item + */ +export class ClaimedTaskActionsReturnToPoolComponent extends ClaimedTaskActionsAbstractComponent { -export class ClaimedTaskActionsReturnToPoolComponent { + constructor(protected claimedTaskService: ClaimedTaskDataService) { + super(); + } /** - * A boolean representing if a return to pool operation is pending + * Return task to pool */ - @Input() processingReturnToPool: boolean; - - /** - * CSS classes to append to return to pool button - */ - @Input() wrapperClass: string; - - /** - * An event fired when a return to pool action is confirmed. - */ - @Output() returnToPool: EventEmitter = new EventEmitter(); - - /** - * Emit returnToPool event - */ - confirmReturnToPool() { - this.returnToPool.emit(); + process() { + this.processing$.next(true); + this.claimedTaskService.returnToPoolTask(this.object.id) + .subscribe((res: ProcessTaskResponse) => { + this.processing$.next(false); + this.processCompleted.emit(res.hasSucceeded); + }); } } 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 new file mode 100644 index 0000000000..a115c4e5b8 --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-decorator.ts @@ -0,0 +1,23 @@ +import { hasNoValue } from '../../../empty.util'; + +const map = 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); + } else { + throw new Error(`There can't be more than one component to render ClaimedTaskActions for option "${option}"`); + } + }; +} + +/** + * Get the component used for rendering a ClaimedTaskActions page by option type + */ +export function getComponentByWorkflowTaskOption(option: string) { + return map.get(option); +} diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html new file mode 100644 index 0000000000..364443c47f --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.html @@ -0,0 +1 @@ + diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts new file mode 100644 index 0000000000..d8c8ecccec --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component.ts @@ -0,0 +1,85 @@ +import { + Component, + ComponentFactoryResolver, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, + ViewChild +} from '@angular/core'; +import { getComponentByWorkflowTaskOption } from './claimed-task-actions-decorator'; +import { ClaimedTask } from '../../../../core/tasks/models/claimed-task-object.model'; +import { ClaimedTaskActionsDirective } from './claimed-task-actions.directive'; +import { ClaimedTaskActionsAbstractComponent } from '../abstract/claimed-task-actions-abstract.component'; +import { hasValue } from '../../../empty.util'; +import { Subscription } from 'rxjs/internal/Subscription'; + +@Component({ + selector: 'ds-claimed-task-actions-loader', + templateUrl: './claimed-task-actions-loader.component.html' +}) +/** + * Component for loading a ClaimedTaskAction component depending on the "option" input + * Passes on the ClaimedTask to the component and subscribes to the processCompleted output + */ +export class ClaimedTaskActionsLoaderComponent implements OnInit, OnDestroy { + /** + * The ClaimedTask object + */ + @Input() object: ClaimedTask; + + /** + * The name of the option to render + * Passed on to the decorator to fetch the relevant component for this option + */ + @Input() option: string; + + /** + * Emits the success or failure of a processed action + */ + @Output() processCompleted: EventEmitter = new EventEmitter(); + + /** + * Directive to determine where the dynamic child component is located + */ + @ViewChild(ClaimedTaskActionsDirective, {static: true}) claimedTaskActionsDirective: ClaimedTaskActionsDirective; + + /** + * Array to track all subscriptions and unsubscribe them onDestroy + * @type {Array} + */ + protected subs: Subscription[] = []; + + constructor(private componentFactoryResolver: ComponentFactoryResolver) { + } + + /** + * Fetch, create and initialize the relevant component + */ + ngOnInit(): void { + const comp = getComponentByWorkflowTaskOption(this.option); + if (hasValue(comp)) { + const componentFactory = this.componentFactoryResolver.resolveComponentFactory(comp); + + const viewContainerRef = this.claimedTaskActionsDirective.viewContainerRef; + viewContainerRef.clear(); + + const componentRef = viewContainerRef.createComponent(componentFactory); + const componentInstance = (componentRef.instance as ClaimedTaskActionsAbstractComponent); + componentInstance.object = this.object; + if (hasValue(componentInstance.processCompleted)) { + this.subs.push(componentInstance.processCompleted.subscribe((success) => this.processCompleted.emit(success))); + } + } + } + + /** + * Unsubscribe from open subscriptions + */ + ngOnDestroy(): void { + this.subs + .filter((subscription) => hasValue(subscription)) + .forEach((subscription) => subscription.unsubscribe()); + } +} diff --git a/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts new file mode 100644 index 0000000000..a4a55b541b --- /dev/null +++ b/src/app/shared/mydspace-actions/claimed-task/switcher/claimed-task-actions.directive.ts @@ -0,0 +1,11 @@ +import { Directive, ViewContainerRef } from '@angular/core'; + +@Directive({ + selector: '[dsClaimedTaskActions]', +}) +/** + * Directive used as a hook to know where to inject the dynamic Claimed Task Actions component + */ +export class ClaimedTaskActionsDirective { + constructor(public viewContainerRef: ViewContainerRef) { } +} diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c6180e3a3b..435f15f5ef 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -177,6 +177,9 @@ import { ImportableListItemControlComponent } from './object-collection/shared/i import { DragDropModule } from '@angular/cdk/drag-drop'; import { ExistingMetadataListElementComponent } from './form/builder/ds-dynamic-form-ui/existing-metadata-list-element/existing-metadata-list-element.component'; import { SortablejsModule } from 'ngx-sortablejs'; +import { ClaimedTaskActionsLoaderComponent } from './mydspace-actions/claimed-task/switcher/claimed-task-actions-loader.component'; +import { ClaimedTaskActionsDirective } from './mydspace-actions/claimed-task/switcher/claimed-task-actions.directive'; +import { ClaimedTaskActionsEditMetadataComponent } from './mydspace-actions/claimed-task/edit-metadata/claimed-task-actions-edit-metadata.component'; const MODULES = [ // Do NOT include UniversalModule, HttpModule, or JsonpModule here @@ -279,6 +282,8 @@ const COMPONENTS = [ ClaimedTaskActionsApproveComponent, ClaimedTaskActionsRejectComponent, ClaimedTaskActionsReturnToPoolComponent, + ClaimedTaskActionsEditMetadataComponent, + ClaimedTaskActionsLoaderComponent, ItemActionsComponent, PoolTaskActionsComponent, WorkflowitemActionsComponent, @@ -402,7 +407,11 @@ const ENTRY_COMPONENTS = [ DsDynamicLookupRelationSearchTabComponent, DsDynamicLookupRelationSelectionTabComponent, DsDynamicLookupRelationExternalSourceTabComponent, - ExternalSourceEntryImportModalComponent + ExternalSourceEntryImportModalComponent, + ClaimedTaskActionsApproveComponent, + ClaimedTaskActionsRejectComponent, + ClaimedTaskActionsReturnToPoolComponent, + ClaimedTaskActionsEditMetadataComponent ]; const SHARED_ITEM_PAGE_COMPONENTS = [ @@ -430,7 +439,8 @@ const DIRECTIVES = [ AutoFocusDirective, RoleDirective, MetadataRepresentationDirective, - ListableObjectDirective + ListableObjectDirective, + ClaimedTaskActionsDirective ]; @NgModule({