Skip to content
This repository has been archived by the owner on May 7, 2021. It is now read-only.

Commit

Permalink
feat(delete-work-item): add delete work item from list (#2797)
Browse files Browse the repository at this point in the history
* feat(delete-work-item): add delete work item from query list, preview and detail page

* feat(delete-work-item): add test for delete work item

* feat(delete-work-item):  add internal user level feature flag

* fix(test): fix delete work item test
  • Loading branch information
divyanshiGupta authored and nimishamukherjee committed Nov 8, 2018
1 parent e3271db commit c45d8bc
Show file tree
Hide file tree
Showing 24 changed files with 267 additions and 18 deletions.
29 changes: 28 additions & 1 deletion src/app/actions/work-item.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export const RESET_WORKITEMS = '[workItem] Reset WorkItems';
export const GET_MORE_WORKITEMS = '[workItems] Get More WorkItems';
export const GET_MORE_WORKITEMS_SUCCESS = '[workItems] Get More WorkItems Success';
export const NEXT_LINK_SUCCESS = '[workItem] Next Link Success';
export const DELETE = '[workItem] Delete';
export const DELETE_SUCCESS = '[workItem] DeleteSuccess';
export const DELETE_ERROR = '[workItem] DeleteError';

export class Add implements Action {
payload: {workItem: WorkItemService, createId: number, parentId: string, openDetailPage: boolean};
Expand Down Expand Up @@ -199,6 +202,26 @@ export class GetMoreWorkItemsSuccess implements Action {
readonly type = GET_MORE_WORKITEMS_SUCCESS;
}

export class Delete implements Action {
payload: WorkItemUI;
constructor(payload: WorkItemUI) {
this.payload = payload;
}
readonly type = DELETE;
}

export class DeleteSuccess implements Action {
payload: WorkItemUI;
constructor(payload: WorkItemUI) {
this.payload = payload;
}
readonly type = DELETE_SUCCESS;
}

export class DeleteError implements Action {
readonly type = DELETE_ERROR;
}

export type All
= Add
| Get
Expand All @@ -218,4 +241,8 @@ export type All
| DeleteLink
| ResetWorkItems
| GetMoreWorkItems
| GetMoreWorkItemsSuccess;
| GetMoreWorkItemsSuccess
| Delete
| DeleteSuccess
| DeleteError;

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<f8-feature-toggle featureName="Planner.deleteWorkItem" [userLevel]="internal"></f8-feature-toggle>
<ng-template #internal>
<span *ngIf="allowDelete | async"
id="wi-delete-icon"
class="pficon-delete delete-icon pointer"
placement="right"
tooltip="Delete work item"
(click)="deleteWorkItem($event)">
</span>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import '../../../assets/stylesheets/shared/_variables.less';

.delete-icon {
color: @color-pf-red-100;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { first } from 'rxjs/operators';
import { Delete } from '../../actions/work-item.actions';
import { PermissionQuery } from '../../models/permission.model';
import { WorkItemUI } from '../../models/work-item';
import { ModalService } from '../../services/modal.service';
import { AppState } from '../../states/app.state';

@Component({
selector: 'f8-delete-workitem',
templateUrl: './delete-work-item.component.html',
styleUrls: ['./delete-work-item.component.less']
})

export class DeleteWorkItemComponent {

@Input('workItem') set workItemInput(val: WorkItemUI) {
if (val) {
this.workItem = val;
this.allowDelete =
this.permissionQuery.isAllowedToDelete(val);
}
}
@Input() detailContext: string = '';

@Output() readonly onDelete: EventEmitter<any> = new EventEmitter;

allowDelete: Observable<boolean>;
workItem: WorkItemUI;

constructor(
private modalService: ModalService,
private store: Store<AppState>,
private permissionQuery: PermissionQuery
) {}

deleteWorkItem(event: MouseEvent): void {
let note = 'Are you sure you want to delete this work item?';
if (this.workItem.hasChildren) {
note = 'This work item has children. ' + note;
}
this.modalService.openModal('Delete Work Item', note, 'Delete', 'deleteWorkItem')
.pipe(
first()
).subscribe((actionKey: string) => {
if (actionKey === 'deleteWorkItem') {
this.onDelete.emit();
this.store.dispatch(new Delete(this.workItem));
}
});
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { TooltipConfig, TooltipModule } from 'ngx-bootstrap/tooltip';
import { FeatureFlagModule } from 'ngx-feature-flag';
import { ModalService } from '../../services/modal.service';
import { DeleteWorkItemComponent } from './delete-work-item.component';

@NgModule({
imports: [
CommonModule,
TooltipModule,
FeatureFlagModule
],
declarations: [
DeleteWorkItemComponent
],
exports: [
DeleteWorkItemComponent
],
providers: [
ModalService,
TooltipConfig
]
})

export class DeleteWorkItemModule {}

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ <h4>Please wait, we are loading your data.</h4>
</div>

<work-item-preview-panel #quickPreview
(onOpen)="isQuickPreviewOpen = true"
(onClose)="isQuickPreviewOpen = false"
[context]="'query'">
</work-item-preview-panel>

Expand Down Expand Up @@ -107,6 +109,7 @@ <h4>Please wait, we are loading your data.</h4>
[context]="'query'"
(onQuickPreview)="onPreview($event)"
(onChildExploration)="onChildExploration($event)"
(onDelete)="isQuickPreviewOpen ? closePreview() : ''"
[class.f8-wi__table-config]="c.prop === 'label' || c.prop === 'assignees'"
[class.planner-hack-title-truncate]="c.prop === 'title'">
</work-item-cell>
Expand All @@ -121,3 +124,5 @@ <h4>Please wait, we are loading your data.</h4>
</div>
</div>
</div>

<osio-modal></osio-modal>
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@
}

work-item-cell {
max-width: ~'calc(e("100% - 20px"))';
max-width: ~'calc(e("100% - 50px"))';
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export class PlannerQueryComponent implements OnInit, OnDestroy, AfterViewChecke
// This variable stores the number of items
// Scroll is already checked for
private scrollCheckedFor: number = 0;
private isQuickPreviewOpen: boolean;

constructor(
private cookieService: CookieService,
Expand Down Expand Up @@ -136,6 +137,10 @@ export class PlannerQueryComponent implements OnInit, OnDestroy, AfterViewChecke
this.quickPreview.open(workItem);
}

closePreview(): void {
this.quickPreview.close();
}

setDataTableColumns() {
// Cookie for datatableColumn config
if (!this.cookieService.getCookie(datatableColumn.length).status) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { FilterColumnModule } from '../../pipes/column-filter.module';
import { CookieService } from '../../services/cookie.service';
import { FilterService } from '../../services/filter.service';
import { InlineInputModule } from '../../widgets/inlineinput/inlineinput.module';
import { PlannerModalModule } from '../../widgets/modal/modal.module';
import { WorkItemCellModule } from '../work-item-cell/work-item-cell.module';
import { WorkItemPreviewPanelModule } from '../work-item-preview-panel/work-item-preview-panel.module';
import { PlannerQueryRoutingModule } from './planner-query-routing.module';
Expand Down Expand Up @@ -44,7 +45,8 @@ import { WorkItemQuickAddModule } from '../work-item-quick-add/work-item-quick-a
WorkItemPreviewPanelModule,
WorkItemQuickAddModule,
InfiniteScrollModule,
NgLetModule
NgLetModule,
PlannerModalModule
],
declarations: [PlannerQueryComponent],
exports: [PlannerQueryComponent],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
placement="right"
tooltip="Open Detail View"
[routerLink]="context === 'list' ? ['detail', row.number] : ['../', 'detail', row.number]"></a>
<f8-delete-workitem
*ngIf="context === 'query'"
class="margin-left-10 padding-v-5"
[workItem]="row"
(onDelete)="onDelete.emit()">
</f8-delete-workitem>
</div>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class WorkItemCellComponent {
@Output() readonly onQuickPreview = new EventEmitter();
@Output() readonly clickLabel = new EventEmitter();
@Output() readonly onChildExploration = new EventEmitter();
@Output() readonly onDelete = new EventEmitter();


onDetail(Event: MouseEvent, id: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UserAvatarModule } from '../../widgets/user-avatar/user-avatar.module';

import { RouterModule } from '@angular/router';
import { TruncateModule } from 'ng2-truncate';
import { DeleteWorkItemModule } from '../delete-work-item/delete-work-item.module';
import { AssigneesModule } from './../assignee/assignee.module';
import { LabelsModule } from './../labels/labels.module';
import { WorkItemCellComponent } from './work-item-cell.component';
Expand All @@ -21,7 +22,8 @@ import { WorkItemCellComponent } from './work-item-cell.component';
TooltipModule,
TruncateModule,
UserAvatarModule,
WidgetsModule
WidgetsModule,
DeleteWorkItemModule
],
declarations: [WorkItemCellComponent],
exports: [WorkItemCellComponent],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
</span>
<div *ngIf="detailContext=='preview'">
<div class="pull-left">
<f8-delete-workitem *ngIf="context === 'query'"
class="padding-right-5"
(onDelete)="closeDetail()"
[workItem]="workItem">
</f8-delete-workitem>
<b>#{{ workItem.number }}</b>
<span class="detail-created-date created-by"
*ngIf="workItem.id">
Expand All @@ -30,6 +35,11 @@
</div>
</div>
<div *ngIf="detailContext=='detail'">
<f8-delete-workitem
class="padding-right-5"
(onDelete)="closeDetail()"
[workItem]="workItem">
</f8-delete-workitem>
<b>#{{ workItem.number }}</b>
<span class="detail-created-date created-by"
*ngIf="workItem.id">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { ErrorHandler } from '../../effects/work-item-utils';
import { AreaQuery } from '../../models/area.model';
import { IterationQuery } from '../../models/iteration.model';
import { WorkItemTypeQuery } from '../../models/work-item-type';
import { DeleteWorkItemModule } from '../delete-work-item/delete-work-item.module';
import { CommentQuery } from './../../models/comment';
import { LabelQuery } from './../../models/label.model';
import { SpaceQuery } from './../../models/space';
Expand Down Expand Up @@ -86,6 +87,7 @@ import { UserAvatarModule } from './../../widgets/user-avatar/user-avatar.module
WorkItemLinkModule,
ReactiveFormsModule,
ClickOutModule,
DeleteWorkItemModule,
StoreModule.forFeature('detailPage', {
comments: CommentReducer,
workItem: DetailWorkItemReducer,
Expand Down
20 changes: 19 additions & 1 deletion src/app/effects/work-item.effects.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { Actions, Effect } from '@ngrx/effects';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { select, Store } from '@ngrx/store';
import { Notification, Notifications, NotificationType } from 'ngx-base';
import { empty, Observable, of } from 'rxjs';
Expand Down Expand Up @@ -410,4 +411,21 @@ export class WorkItemEffects {
);
})
);

@Effect() deleteWorkItem$: Observable<Action> = this.actions$
.pipe(
ofType(WorkItemActions.DELETE),
switchMap((action: WorkItemActions.Delete) => {
const workItem = this.workItemMapper.toServiceModel(action.payload);
return this.workItemService.delete(workItem)
.pipe(
map(() => {
return new WorkItemActions.DeleteSuccess(action.payload);
}),
catchError((err: HttpErrorResponse) => this.errHandler.handleError<Action>(
err, `Problem in Deleting work item.`, new WorkItemActions.DeleteError()
))
);
})
);
}
17 changes: 17 additions & 0 deletions src/app/models/permission.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Observable } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { SpaceQuery } from './space';
import { UserQuery } from './user';
import { WorkItemUI } from './work-item';

@Injectable()
export class PermissionQuery {
Expand Down Expand Up @@ -38,4 +39,20 @@ export class PermissionQuery {
})
);
}

isAllowedToDelete(workItem): Observable<boolean> {
return this.spaceQuery.getCurrentSpace
.pipe(
switchMap((space) => {
return this.userService.loggedInUser.pipe(map(user => {
let spaceOwnerId = space.relationships['owned-by'].data.id;
if (spaceOwnerId === user.id || workItem.creator === user.id) {
return true;
} else {
return false;
}
}));
})
);
}
}
14 changes: 14 additions & 0 deletions src/app/reducers/work-item.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ export const WorkItemReducer: ActionReducer<WorkItemState> = (state = initialSta
return workItemAdapter.removeAll(state);
}

case WorkItemActions.DELETE_SUCCESS: {
let newState = {...state};
let deletedWorkItem = action.payload;
newState = {
nextLink: newState.nextLink,
...workItemAdapter.removeOne(deletedWorkItem.id, newState)
};
return {...newState};
}

case WorkItemActions.DELETE_ERROR: {
return {...state};
}

default: {
return state;
}
Expand Down
Loading

0 comments on commit c45d8bc

Please sign in to comment.