mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
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
This commit is contained in:
@@ -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({
|
||||
|
40
src/app/core/data/workflow-action-data.service.ts
Normal file
40
src/app/core/data/workflow-action-data.service.ts
Normal file
@@ -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<WorkflowAction> {
|
||||
protected linkPath = 'workflowactions';
|
||||
|
||||
constructor(
|
||||
protected requestService: RequestService,
|
||||
protected rdbService: RemoteDataBuildService,
|
||||
protected dataBuildService: NormalizedObjectBuildService,
|
||||
protected store: Store<CoreState>,
|
||||
protected objectCache: ObjectCacheService,
|
||||
protected halService: HALEndpointService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected http: HttpClient,
|
||||
protected comparator: DefaultChangeAnalyzer<WorkflowAction>) {
|
||||
super();
|
||||
}
|
||||
|
||||
getBrowseEndpoint(options: FindListOptions, linkPath?: string): Observable<string> {
|
||||
return this.halService.getEndpoint(this.linkPath);
|
||||
}
|
||||
}
|
@@ -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<WorkflowAction> {
|
||||
/**
|
||||
* The workflow action's identifier
|
||||
*/
|
||||
@autoserialize
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* The options available for this workflow action
|
||||
*/
|
||||
@autoserialize
|
||||
options: string[];
|
||||
}
|
19
src/app/core/tasks/models/workflow-action-object.model.ts
Normal file
19
src/app/core/tasks/models/workflow-action-object.model.ts
Normal file
@@ -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[];
|
||||
}
|
@@ -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<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
/**
|
||||
* A boolean representing if the operation is pending
|
||||
*/
|
||||
processing$ = new BehaviorSubject<boolean>(false);
|
||||
|
||||
/**
|
||||
* Method called when the action's button is clicked
|
||||
*/
|
||||
abstract process();
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
<button type="button"
|
||||
[className]="'btn btn-success ' + wrapperClass"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.approve_help' | translate}}"
|
||||
[disabled]="processingApprove"
|
||||
(click)="confirmApprove()">
|
||||
<span *ngIf="processingApprove"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!processingApprove"><i class="fa fa-thumbs-up"></i> {{'submission.workflow.tasks.claimed.approve' | translate}}</span>
|
||||
[disabled]="processing$ | async"
|
||||
(click)="process()">
|
||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!(processing$ | async)"><i class="fa fa-thumbs-up"></i> {{'submission.workflow.tasks.claimed.approve' | translate}}</span>
|
||||
</button>
|
||||
|
@@ -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<any> = new EventEmitter<any>();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -1,20 +1,13 @@
|
||||
|
||||
<a [class.disabled]="!(object.workflowitem | async)?.hasSucceeded"
|
||||
class="btn btn-primary mt-1 mb-3"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
||||
[routerLink]="['/workflowitems/' + (object?.workflowitem | async)?.payload?.id + '/edit']"
|
||||
role="button">
|
||||
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
||||
</a>
|
||||
|
||||
<ds-claimed-task-actions-approve [processingApprove]="(processingApprove$ | async)"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(approve)="approve()"></ds-claimed-task-actions-approve>
|
||||
|
||||
<ds-claimed-task-actions-reject [processingReject]="(processingReject$ | async)"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(reject)="reject($event)"></ds-claimed-task-actions-reject>
|
||||
|
||||
<ds-claimed-task-actions-return-to-pool [processingReturnToPool]="(processingReturnToPool$ | async)"
|
||||
[wrapperClass]="'mt-1 mb-3'"
|
||||
(returnToPool)="returnToPool()"></ds-claimed-task-actions-return-to-pool>
|
||||
<ng-container *ngVar="(actionRD$ | async)?.payload as workflowAction">
|
||||
<div class="mt-1 mb-3">
|
||||
<ds-claimed-task-actions-loader *ngFor="let option of workflowAction?.options"
|
||||
[option]="option"
|
||||
[object]="object"
|
||||
(processCompleted)="handleActionResponse($event)">
|
||||
</ds-claimed-task-actions-loader>
|
||||
<ds-claimed-task-actions-loader [option]="returnToPoolOption"
|
||||
[object]="object"
|
||||
(processCompleted)="handleActionResponse($event)">
|
||||
</ds-claimed-task-actions-loader>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
@@ -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<Claime
|
||||
public workflowitem$: Observable<WorkflowItem>;
|
||||
|
||||
/**
|
||||
* A boolean representing if an approve operation is pending
|
||||
* The workflow action available for this task
|
||||
*/
|
||||
public processingApprove$ = new BehaviorSubject<boolean>(false);
|
||||
public actionRD$: Observable<RemoteData<WorkflowAction>>;
|
||||
|
||||
/**
|
||||
* 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<boolean>(false);
|
||||
|
||||
/**
|
||||
* A boolean representing if a return to pool operation is pending
|
||||
*/
|
||||
public processingReturnToPool$ = new BehaviorSubject<boolean>(false);
|
||||
public returnToPoolOption = WORKFLOW_TASK_OPTION_RETURN;
|
||||
|
||||
/**
|
||||
* Initialize instance variables
|
||||
@@ -60,13 +58,15 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
* @param {TranslateService} translate
|
||||
* @param {SearchService} searchService
|
||||
* @param {RequestService} requestService
|
||||
* @param workflowActionService
|
||||
*/
|
||||
constructor(protected injector: Injector,
|
||||
protected router: Router,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected translate: TranslateService,
|
||||
protected searchService: SearchService,
|
||||
protected requestService: RequestService) {
|
||||
protected requestService: RequestService,
|
||||
protected workflowActionService: WorkflowActionDataService) {
|
||||
super(ClaimedTask.type, injector, router, notificationsService, translate, searchService, requestService);
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.initObjects(this.object);
|
||||
this.initAction(this.object);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,39 +91,12 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve the task.
|
||||
* Init the WorkflowAction
|
||||
*
|
||||
* @param object
|
||||
*/
|
||||
approve() {
|
||||
this.processingApprove$.next(true);
|
||||
this.objectDataService.approveTask(this.object.id)
|
||||
.subscribe((res: ProcessTaskResponse) => {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,7 @@
|
||||
<a *ngIf="object"
|
||||
class="btn btn-primary"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.edit_help' | translate}}"
|
||||
[routerLink]="['/workflowitems/' + (object.workflowitem | async)?.payload.id + '/edit']"
|
||||
role="button">
|
||||
<i class="fa fa-edit"></i> {{'submission.workflow.tasks.claimed.edit' | translate}}
|
||||
</a>
|
@@ -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<ClaimedTaskActionsEditMetadataComponent>;
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
});
|
@@ -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
|
||||
}
|
||||
}
|
@@ -1,10 +1,10 @@
|
||||
<ng-template #rejectTipContent><p [innerHTML]="'submission.workflow.tasks.claimed.reject_help' | translate"></p></ng-template>
|
||||
<button [className]="'btn btn-danger ' + wrapperClass"
|
||||
[ngbTooltip]="rejectTipContent"
|
||||
[disabled]="processingReject"
|
||||
[disabled]="processing$ | async"
|
||||
(click)="openRejectModal(rejectModal)" >
|
||||
<span *ngIf="processingReject"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!processingReject"><i class="fa fa-trash"></i> {{'submission.workflow.tasks.claimed.reject.submit' | translate}}</span>
|
||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!(processing$ | async)"><i class="fa fa-trash"></i> {{'submission.workflow.tasks.claimed.reject.submit' | translate}}</span>
|
||||
</button>
|
||||
|
||||
<ng-template #rejectModal let-c="close" let-d="dismiss">
|
||||
@@ -21,17 +21,17 @@
|
||||
<div class="alert alert-info" role="alert">
|
||||
{{'submission.workflow.tasks.claimed.reject.reason.info' | translate}}
|
||||
</div>
|
||||
<form (ngSubmit)="confirmReject(rejectModal);" [formGroup]="rejectForm" >
|
||||
<form (ngSubmit)="process(rejectModal);" [formGroup]="rejectForm" >
|
||||
<textarea style="width: 100%"
|
||||
formControlName="reason"
|
||||
rows="4"
|
||||
placeholder="{{'submission.workflow.tasks.claimed.reject.reason.placeholder' | translate}}"></textarea>
|
||||
<button id="btn-chat"
|
||||
class="btn btn-danger btn-lg btn-block mt-3"
|
||||
[disabled]="!rejectForm.valid || processingReject"
|
||||
[disabled]="!rejectForm.valid || (processing$ | async)"
|
||||
type="submit">
|
||||
<span *ngIf="processingReject"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!processingReject">{{'submission.workflow.tasks.claimed.reject.reason.submit' | translate}}</span>
|
||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!(processing$ | async)">{{'submission.workflow.tasks.claimed.reject.reason.submit' | translate}}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -1,8 +1,8 @@
|
||||
<button type="button"
|
||||
[className]="'btn btn-secondary ' + wrapperClass"
|
||||
ngbTooltip="{{'submission.workflow.tasks.claimed.return_help' | translate}}"
|
||||
[disabled]="processingReturnToPool"
|
||||
(click)="confirmReturnToPool()">
|
||||
<span *ngIf="processingReturnToPool"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!processingReturnToPool"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span>
|
||||
[disabled]="processing$ | async"
|
||||
(click)="process()">
|
||||
<span *ngIf="processing$ | async"><i class='fas fa-circle-notch fa-spin'></i> {{'submission.workflow.tasks.generic.processing' | translate}}</span>
|
||||
<span *ngIf="!(processing$ | async)"><i class="fa fa-undo"></i> {{'submission.workflow.tasks.claimed.return' | translate}}</span>
|
||||
</button>
|
||||
|
@@ -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<any> = new EventEmitter<any>();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
@@ -0,0 +1 @@
|
||||
<ng-template dsClaimedTaskActions></ng-template>
|
@@ -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<boolean> = new EventEmitter<boolean>();
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
@@ -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) { }
|
||||
}
|
@@ -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({
|
||||
|
Reference in New Issue
Block a user