mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[CST-4320] No way to view an Item when in simple "Approve/Reject" workflow stage
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
import { ThemedComponent } from '../../shared/theme-support/themed.component';
|
||||
import { Component } from '@angular/core';
|
||||
import { WorkflowItemViewComponent } from './workflow-item-view.component';
|
||||
|
||||
/**
|
||||
* Themed wrapper for WorkflowItemViewComponent
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'ds-themed-workflow-item-view',
|
||||
styleUrls: [],
|
||||
templateUrl: './../../shared/theme-support/themed.component.html'
|
||||
})
|
||||
export class ThemedWorkflowItemViewComponent extends ThemedComponent<WorkflowItemViewComponent> {
|
||||
protected getComponentName(): string {
|
||||
return 'WorkflowItemViewComponent';
|
||||
}
|
||||
|
||||
protected importThemedComponent(themeName: string): Promise<any> {
|
||||
return import(`../../../themes/${themeName}/app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component`);
|
||||
}
|
||||
|
||||
protected importUnthemedComponent(): Promise<any> {
|
||||
return import(`./workflow-item-view.component`);
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
<div class="container" *ngVar="(itemRD$ | async) as itemRD">
|
||||
<div class="item-page" *ngIf="itemRD?.hasSucceeded">
|
||||
<div *ngIf="itemRD?.payload as item">
|
||||
<ds-untyped-item [object]="item"></ds-untyped-item>
|
||||
</div>
|
||||
</div>
|
||||
<ds-error *ngIf="itemRD?.hasFailed" message="{{'error.item' | translate}}"></ds-error>
|
||||
<ds-loading *ngIf="itemRD?.isLoading" message="{{'loading.item' | translate}}"></ds-loading>
|
||||
</div>
|
@@ -0,0 +1,114 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { RouteService } from '../../core/services/route.service';
|
||||
import { NotificationsService } from '../../shared/notifications/notifications.service';
|
||||
import { WorkflowItem } from '../../core/submission/models/workflowitem.model';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { VarDirective } from '../../shared/utils/var.directive';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RequestService } from '../../core/data/request.service';
|
||||
import {
|
||||
createFailedRemoteDataObject$,
|
||||
createPendingRemoteDataObject$,
|
||||
createSuccessfulRemoteDataObject,
|
||||
createSuccessfulRemoteDataObject$
|
||||
} from '../../shared/remote-data.utils';
|
||||
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 { getMockRequestService } from '../../shared/mocks/request.service.mock';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { createPaginatedList } from '../../shared/testing/utils.test';
|
||||
import { createRelationshipsObservable } from '../../+item-page/simple/item-types/shared/item.component.spec';
|
||||
import { WorkflowItemViewComponent } from './workflow-item-view.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('WorkflowItemViewComponent', () => {
|
||||
|
||||
const mockItem: Item = Object.assign(new Item(), {
|
||||
bundles: createSuccessfulRemoteDataObject$(createPaginatedList([])),
|
||||
metadata: [],
|
||||
relationships: createRelationshipsObservable()
|
||||
});
|
||||
|
||||
const mockWfi = new WorkflowItem();
|
||||
mockWfi.item = createSuccessfulRemoteDataObject$(mockItem);
|
||||
|
||||
const mockRoute = Object.assign(new ActivatedRouteStub(), {
|
||||
data: observableOf({ wfi: createSuccessfulRemoteDataObject(mockWfi) })
|
||||
});
|
||||
|
||||
let comp: WorkflowItemViewComponent;
|
||||
let fixture: ComponentFixture<WorkflowItemViewComponent>;
|
||||
let wfi;
|
||||
let itemRD$;
|
||||
let id;
|
||||
|
||||
function init() {
|
||||
itemRD$ = createSuccessfulRemoteDataObject$(itemRD$);
|
||||
wfi = new WorkflowItem();
|
||||
wfi.item = itemRD$;
|
||||
id = 'de11b5e5-064a-4e98-a7ac-a1a6a65ddf80';
|
||||
}
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [TranslateModule.forRoot({
|
||||
loader: {
|
||||
provide: TranslateLoader,
|
||||
useClass: TranslateLoaderMock
|
||||
}
|
||||
})],
|
||||
declarations: [WorkflowItemViewComponent, VarDirective],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: mockRoute },
|
||||
{ provide: Router, useClass: RouterStub },
|
||||
{ provide: RouteService, useValue: {} },
|
||||
{ provide: NotificationsService, useClass: NotificationsServiceStub },
|
||||
{ provide: RequestService, useValue: getMockRequestService() },
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
fixture = TestBed.createComponent(WorkflowItemViewComponent);
|
||||
comp = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should create', () => {
|
||||
expect(comp).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('when the item is loading', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = createPendingRemoteDataObject$();
|
||||
// comp.itemRD$ = observableOf(new RemoteData(true, true, true, null, undefined));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display a loading component', () => {
|
||||
const loading = fixture.debugElement.query(By.css('ds-loading'));
|
||||
expect(loading.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the item failed loading', () => {
|
||||
beforeEach(() => {
|
||||
comp.itemRD$ = createFailedRemoteDataObject$('server error', 500);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display an error component', () => {
|
||||
const error = fixture.debugElement.query(By.css('ds-error'));
|
||||
expect(error.nativeElement).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -0,0 +1,40 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ActivatedRoute, Data } from '@angular/router';
|
||||
import { map, switchMap } from 'rxjs/operators';import { RemoteData } from '../../core/data/remote-data';
|
||||
|
||||
import {
|
||||
getAllSucceededRemoteData,
|
||||
getRemoteDataPayload
|
||||
} from '../../core/shared/operators';
|
||||
import { WorkflowItem } from '../../core/submission/models/workflowitem.model';
|
||||
import { Item } from '../../core/shared/item.model';
|
||||
import { ViewMode } from '../../core/shared/view-mode.model';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-workflow-item-view',
|
||||
templateUrl: './workflow-item-view.component.html'
|
||||
})
|
||||
/**
|
||||
* Component representing a page to delete a workflow item
|
||||
*/
|
||||
export class WorkflowItemViewComponent implements OnInit {
|
||||
|
||||
public wfi$: Observable<WorkflowItem>;
|
||||
public itemRD$: Observable<RemoteData<Item>>;
|
||||
|
||||
/**
|
||||
* The view-mode we're currently on
|
||||
*/
|
||||
viewMode = ViewMode.StandalonePage;
|
||||
|
||||
|
||||
constructor(protected route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.wfi$ = this.route.data.pipe(map((data: Data) => data.wfi as RemoteData<WorkflowItem>), getRemoteDataPayload());
|
||||
this.itemRD$ = this.wfi$.pipe(switchMap((wfi: WorkflowItem) => (wfi.item as Observable<RemoteData<Item>>).pipe(getAllSucceededRemoteData())));
|
||||
}
|
||||
|
||||
}
|
@@ -8,6 +8,9 @@ export function getWorkflowItemPageRoute(wfiId: string) {
|
||||
export function getWorkflowItemEditRoute(wfiId: string) {
|
||||
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_EDIT_PATH).toString();
|
||||
}
|
||||
export function getWorkflowItemViewRoute(wfiId: string) {
|
||||
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_VIEW_PATH).toString();
|
||||
}
|
||||
|
||||
export function getWorkflowItemDeleteRoute(wfiId: string) {
|
||||
return new URLCombiner(getWorkflowItemModuleRoute(), wfiId, WORKFLOW_ITEM_DELETE_PATH).toString();
|
||||
@@ -19,4 +22,5 @@ export function getWorkflowItemSendBackRoute(wfiId: string) {
|
||||
|
||||
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';
|
||||
|
@@ -6,12 +6,14 @@ import { WorkflowItemPageResolver } from './workflow-item-page.resolver';
|
||||
import {
|
||||
WORKFLOW_ITEM_DELETE_PATH,
|
||||
WORKFLOW_ITEM_EDIT_PATH,
|
||||
WORKFLOW_ITEM_SEND_BACK_PATH
|
||||
WORKFLOW_ITEM_SEND_BACK_PATH,
|
||||
WORKFLOW_ITEM_VIEW_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';
|
||||
import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/themed-workflow-item-send-back.component';
|
||||
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||
import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-workflow-item-view.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -29,6 +31,15 @@ import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.reso
|
||||
},
|
||||
data: { title: 'workflow-item.edit.title', breadcrumbKey: 'workflow-item.edit' }
|
||||
},
|
||||
{
|
||||
canActivate: [AuthenticatedGuard],
|
||||
path: WORKFLOW_ITEM_VIEW_PATH,
|
||||
component: ThemedWorkflowItemViewComponent,
|
||||
resolve: {
|
||||
breadcrumb: I18nBreadcrumbResolver
|
||||
},
|
||||
data: { title: 'workflow-item.view.title', breadcrumbKey: 'workflow-item.view' }
|
||||
},
|
||||
{
|
||||
canActivate: [AuthenticatedGuard],
|
||||
path: WORKFLOW_ITEM_DELETE_PATH,
|
||||
|
@@ -7,6 +7,10 @@ import { WorkflowItemDeleteComponent } from './workflow-item-delete/workflow-ite
|
||||
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 { WorkflowItemViewComponent } from './workflow-item-view/workflow-item-view.component';
|
||||
import { ThemedWorkflowItemViewComponent } from './workflow-item-view/themed-workflow-item-view.component';
|
||||
import { StatisticsModule } from '../statistics/statistics.module';
|
||||
import { ItemPageModule } from '../+item-page/item-page.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@@ -14,8 +18,17 @@ import { ThemedWorkflowItemSendBackComponent } from './workflow-item-send-back/t
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
SubmissionModule,
|
||||
StatisticsModule,
|
||||
ItemPageModule
|
||||
],
|
||||
declarations: [WorkflowItemDeleteComponent, ThemedWorkflowItemDeleteComponent, WorkflowItemSendBackComponent, ThemedWorkflowItemSendBackComponent]
|
||||
declarations: [
|
||||
WorkflowItemDeleteComponent,
|
||||
ThemedWorkflowItemDeleteComponent,
|
||||
WorkflowItemSendBackComponent,
|
||||
ThemedWorkflowItemSendBackComponent,
|
||||
WorkflowItemViewComponent,
|
||||
ThemedWorkflowItemViewComponent
|
||||
]
|
||||
})
|
||||
/**
|
||||
* This module handles all modules that need to access the workflowitems edit page.
|
||||
|
@@ -5,6 +5,15 @@
|
||||
[object]="object"
|
||||
(processCompleted)="this.processCompleted.emit($event)">
|
||||
</ds-claimed-task-actions-loader>
|
||||
|
||||
<ng-container *ngIf="hasViewAction(workflowAction)">
|
||||
<button class="btn btn-primary workflow-view"
|
||||
ngbTooltip="{{'submission.workflow.generic.view-help' | translate}}"
|
||||
[routerLink]="[getWorkflowItemViewRoute((workflowitem$ | async))]">
|
||||
<i class="fa fa-info-circle"></i> {{"submission.workflow.generic.view" | translate}}
|
||||
</button>
|
||||
</ng-container>
|
||||
|
||||
<ds-claimed-task-actions-loader [option]="returnToPoolOption"
|
||||
[object]="object"
|
||||
(processCompleted)="this.processCompleted.emit($event)">
|
||||
|
@@ -23,6 +23,7 @@ import { WorkflowActionDataService } from '../../../core/data/workflow-action-da
|
||||
import { WorkflowAction } from '../../../core/tasks/models/workflow-action-object.model';
|
||||
import { VarDirective } from '../../utils/var.directive';
|
||||
import { createSuccessfulRemoteDataObject, createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
let component: ClaimedTaskActionsComponent;
|
||||
let fixture: ComponentFixture<ClaimedTaskActionsComponent>;
|
||||
@@ -81,7 +82,7 @@ function init() {
|
||||
}
|
||||
});
|
||||
rdItem = createSuccessfulRemoteDataObject(item);
|
||||
workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem) });
|
||||
workflowitem = Object.assign(new WorkflowItem(), { item: observableOf(rdItem), id: '333' });
|
||||
rdWorkflowitem = createSuccessfulRemoteDataObject(workflowitem);
|
||||
mockObject = Object.assign(new ClaimedTask(), { workflowitem: observableOf(rdWorkflowitem), id: '1234' });
|
||||
workflowAction = Object.assign(new WorkflowAction(), { id: 'action-1', options: ['option-1', 'option-2'] });
|
||||
@@ -91,7 +92,7 @@ function init() {
|
||||
});
|
||||
}
|
||||
|
||||
describe('ClaimedTaskActionsComponent', () => {
|
||||
fdescribe('ClaimedTaskActionsComponent', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
init();
|
||||
TestBed.configureTestingModule({
|
||||
@@ -159,4 +160,26 @@ describe('ClaimedTaskActionsComponent', () => {
|
||||
expect(notificationsServiceStub.error).toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('when edit options is not available', () => {
|
||||
it('should display a view button', waitForAsync(() => {
|
||||
component.object = null;
|
||||
component.initObjects(mockObject);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
const debugElement = fixture.debugElement.query(By.css('.workflow-view'));
|
||||
expect(debugElement).toBeTruthy();
|
||||
expect(debugElement.nativeElement.innerText).toBe('submission.workflow.generic.view');
|
||||
});
|
||||
|
||||
}));
|
||||
|
||||
it('getWorkflowItemViewRoute should return the combined uri to show a workspaceitem', waitForAsync(() => {
|
||||
const href = component.getWorkflowItemViewRoute(workflowitem);
|
||||
expect(href).toEqual('/workflowitems/333/view');
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
@@ -17,6 +17,7 @@ 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_TO_POOL } from './return-to-pool/claimed-task-actions-return-to-pool.component';
|
||||
import { getWorkflowItemViewRoute } from '../../../+workflowitems-edit-page/workflowitems-edit-page-routing-paths';
|
||||
|
||||
/**
|
||||
* This component represents actions related to ClaimedTask object.
|
||||
@@ -85,6 +86,7 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
*/
|
||||
initObjects(object: ClaimedTask) {
|
||||
this.object = object;
|
||||
|
||||
this.workflowitem$ = (this.object.workflowitem as Observable<RemoteData<WorkflowItem>>).pipe(
|
||||
filter((rd: RemoteData<WorkflowItem>) => ((!rd.isRequestPending) && isNotUndefined(rd.payload))),
|
||||
map((rd: RemoteData<WorkflowItem>) => rd.payload),
|
||||
@@ -100,4 +102,19 @@ export class ClaimedTaskActionsComponent extends MyDSpaceActionsComponent<Claime
|
||||
this.actionRD$ = object.action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if claimed task actions should display a view item button.
|
||||
* @param workflowAction
|
||||
*/
|
||||
hasViewAction(workflowAction: WorkflowAction) {
|
||||
return !workflowAction?.options.includes('submit_edit_metadata');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the workflowitem view route.
|
||||
*/
|
||||
getWorkflowItemViewRoute(workflowitem: WorkflowItem): string {
|
||||
return getWorkflowItemViewRoute(workflowitem?.id);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3588,6 +3588,8 @@
|
||||
|
||||
"workflow-item.edit.title": "Edit workflowitem",
|
||||
|
||||
"workflow-item.edit.title": "View workflowitem",
|
||||
|
||||
"workflow-item.delete.notification.success.title": "Deleted",
|
||||
|
||||
"workflow-item.delete.notification.success.content": "This workflow item was successfully deleted",
|
||||
|
@@ -0,0 +1,14 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { WorkflowItemViewComponent as BaseComponent } from '../../../../../app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-workflow-item-view',
|
||||
// styleUrls: ['workflow-item-view.component.scss'],
|
||||
// templateUrl: './workflow-item-view.component.html'
|
||||
templateUrl: '../../../../../app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component.html'
|
||||
})
|
||||
/**
|
||||
* Component representing a page to view a workflow item
|
||||
*/
|
||||
export class WorkflowItemViewComponent extends BaseComponent {
|
||||
}
|
@@ -79,6 +79,7 @@ import { HeaderComponent } from './app/header/header.component';
|
||||
import { FooterComponent } from './app/footer/footer.component';
|
||||
import { BreadcrumbsComponent } from './app/breadcrumbs/breadcrumbs.component';
|
||||
import { HeaderNavbarWrapperComponent } from './app/header-nav-wrapper/header-navbar-wrapper.component';
|
||||
import { WorkflowItemViewComponent } from './app/+workflowitems-edit-page/workflow-item-view/workflow-item-view.component';
|
||||
|
||||
const DECLARATIONS = [
|
||||
HomePageComponent,
|
||||
@@ -115,6 +116,7 @@ const DECLARATIONS = [
|
||||
SubmissionSubmitComponent,
|
||||
WorkflowItemDeleteComponent,
|
||||
WorkflowItemSendBackComponent,
|
||||
WorkflowItemViewComponent,
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
NavbarComponent,
|
||||
|
Reference in New Issue
Block a user