Skip to content

Commit fcd3b17

Browse files
committed
#67 functionality to delete added
1 parent 782c1cb commit fcd3b17

File tree

7 files changed

+117
-117
lines changed

7 files changed

+117
-117
lines changed

src/app/modules/activities-management/components/activity-list/activity-list.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ <h2 class="mb-0">
1010
</a>
1111
<div class="btn-group float-right" role="group">
1212
<i class="far fa-edit btn btn-link text-white"></i>
13-
<i class="far fa-trash-alt btn btn-link text-white"></i>
13+
<i (click)="deleteActivity(activity.id)" class="far fa-trash-alt btn btn-link text-white"></i>
1414
</div>
1515
</h2>
1616
</div>

src/app/modules/activities-management/components/activity-list/activity-list.component.spec.ts

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,27 @@ import { allActivities } from './../../store/activity-management.selectors';
44
import { ActivityState } from './../../store/activity-management.reducers';
55
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
66
import { ActivityListComponent } from './activity-list.component';
7+
import { HttpClientTestingModule } from '@angular/common/http/testing';
78

89
describe('ActivityListComponent', () => {
910
let component: ActivityListComponent;
1011
let fixture: ComponentFixture<ActivityListComponent>;
1112
let mockActivitiesSelector;
12-
const activityToDelete = { id: '1', name: 'X', description: 'ABC'};
13+
const activityToDelete = { id: '1', name: 'X', description: 'ABC' };
1314

14-
const state = { data: [{id: 'id', name: 'name', description: 'description'}], isLoading: false, message: '' };
15+
const state = { data: [{ id: 'id', name: 'name', description: 'description' }], isLoading: false, message: '' };
1516

1617
let store: MockStore<ActivityState>;
1718

1819
beforeEach(async(() => {
1920
TestBed.configureTestingModule({
20-
declarations: [ ActivityListComponent ],
21-
providers: [ provideMockStore({ initialState: state }) ]
22-
})
23-
.compileComponents();
21+
declarations: [ActivityListComponent],
22+
providers: [provideMockStore({ initialState: state })],
23+
}).compileComponents();
2424

2525
store = TestBed.inject(MockStore);
2626

27-
mockActivitiesSelector = store.overrideSelector( allActivities, state );
27+
mockActivitiesSelector = store.overrideSelector(allActivities, state);
2828
}));
2929

3030
beforeEach(() => {
@@ -37,35 +37,16 @@ describe('ActivityListComponent', () => {
3737
expect(component).toBeTruthy();
3838
});
3939

40-
it('showModal is true onSelectActivityToDelete', () => {
41-
component.onSelectActivityToDelete(activityToDelete);
40+
it('deleteActivity is dispatched in onClick event', () => {
41+
const activityId = 'abcd';
4242

43-
expect(component.showModal).toBeTruthy();
44-
});
45-
46-
it('selectedActivity is propulated onSelectActivityToDelete', () => {
47-
component.onSelectActivityToDelete(activityToDelete);
48-
49-
expect(component.selectedActivity).toEqual(activityToDelete);
50-
});
51-
52-
it('deleteActivity is dispatched onConfirmDeleteActivity', () => {
5343
spyOn(store, 'dispatch');
54-
component.selectedActivity = activityToDelete;
5544

56-
component.onConfirmDeleteActivity();
45+
component.deleteActivity(activityId);
5746

5847
expect(store.dispatch).toHaveBeenCalled();
5948
});
6049

61-
it('showModal is false and selectedProject is null onConfirmDeleteActivity', () => {
62-
component.selectedActivity = activityToDelete;
63-
component.onConfirmDeleteActivity();
64-
65-
expect(component.showModal).toBeFalsy();
66-
expect(component.selectedActivity).toBe(null);
67-
});
68-
6950
it('onInit, LoadActivities action is dispatched', () => {
7051
spyOn(store, 'dispatch');
7152

@@ -80,6 +61,7 @@ describe('ActivityListComponent', () => {
8061
expect(component.activities).toBe(state.data);
8162
});
8263

83-
afterEach(() => { fixture.destroy(); });
84-
64+
afterEach(() => {
65+
fixture.destroy();
66+
});
8567
});

src/app/modules/activities-management/components/activity-list/activity-list.component.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,35 @@ import { Component } from '@angular/core';
33
import { Store, select } from '@ngrx/store';
44

55
import { allActivities } from '../../store';
6-
import { LoadActivities, DeleteActivity } from './../../store/activity-management.actions';
6+
import { LoadActivities, DeleteActivity, DeleteActivitySuccess } from './../../store/activity-management.actions';
77
import { ActivityState } from './../../store/activity-management.reducers';
88
import { Activity } from '../../../shared/models';
9+
import { ActivityService } from '../../services/activity.service';
910

10-
@Component({selector: 'app-activity-list', templateUrl: './activity-list.component.html', styleUrls: ['./activity-list.component.scss']})
11+
@Component({
12+
selector: 'app-activity-list',
13+
templateUrl: './activity-list.component.html',
14+
styleUrls: ['./activity-list.component.scss'],
15+
})
1116
export class ActivityListComponent implements OnInit {
12-
13-
@Input()activities: Activity[] = [];
17+
@Input() activities: Activity[] = [];
1418
public isLoading: boolean;
1519

1620
showModal: boolean;
1721
selectedActivity: Activity;
1822

19-
constructor(private store: Store<ActivityState>) { }
23+
constructor(private store: Store<ActivityState>, private service: ActivityService) {}
2024

2125
ngOnInit() {
2226
this.store.dispatch(new LoadActivities());
2327
const activities$ = this.store.pipe(select(allActivities));
24-
activities$.subscribe(response => {
28+
activities$.subscribe((response) => {
2529
this.isLoading = response.isLoading;
2630
this.activities = response.data;
2731
});
2832
}
2933

30-
onSelectActivityToDelete(activityToDelete: Activity) {
31-
this.showModal = true;
32-
this.selectedActivity = activityToDelete;
33-
}
34-
35-
onConfirmDeleteActivity() {
36-
this.showModal = false;
37-
this.store.dispatch(new DeleteActivity(this.selectedActivity.id));
38-
this.selectedActivity = null;
34+
deleteActivity(activityId: string) {
35+
this.store.dispatch(new DeleteActivity(activityId));
3936
}
4037
}

src/app/modules/activities-management/services/activity.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,9 @@ import { environment } from './../../../../environments/environment';
66
import { Activity } from '../../shared/models';
77

88
@Injectable({
9-
providedIn: 'root'
9+
providedIn: 'root',
1010
})
1111
export class ActivityService {
12-
1312
baseUrl = `${environment.timeTrackerApiUrl}/activities`;
1413

1514
constructor(private http: HttpClient) {}
@@ -18,7 +17,8 @@ export class ActivityService {
1817
return this.http.get<Activity[]>(this.baseUrl);
1918
}
2019

21-
deleteActivity(acitivityId) {
22-
throw new Error('Method not implemented.');
20+
deleteActivity(acitivityId: string) {
21+
const url = `${this.baseUrl}/${acitivityId}`;
22+
return this.http.delete<void>(url);
2323
}
2424
}

src/app/modules/activities-management/store/activity-management.actions.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,41 +3,44 @@ import { Action } from '@ngrx/store';
33
import { Activity } from './../../shared/models/activity.model';
44

55
export enum ActivityManagementActionTypes {
6-
LOAD_ACTIVITIES = '[ActivityManagement] Load Activities',
7-
LOAD_ACTIVITIES_SUCCESS = '[ActivityManagement] Load Activities Successs',
8-
LOAD_ACTIVITIES_FAIL = '[ActivityManagement] Load Activities Fail',
9-
DELETE_ACTIVITY = '[ActivityManagement] Delete Activity',
10-
DELETE_ACTIVITY_SUCCESS = '[ActivityManagement] Delete Activity Success',
6+
LOAD_ACTIVITIES = '[ActivityManagement] Load Activities',
7+
LOAD_ACTIVITIES_SUCCESS = '[ActivityManagement] Load Activities Successs',
8+
LOAD_ACTIVITIES_FAIL = '[ActivityManagement] Load Activities Fail',
9+
DELETE_ACTIVITY = '[ActivityManagement] Delete Activity',
10+
DELETE_ACTIVITY_SUCCESS = '[ActivityManagement] Delete Activity Success',
1111
}
1212

1313
export class DeleteActivity implements Action {
14-
public readonly type = ActivityManagementActionTypes.DELETE_ACTIVITY;
14+
public readonly type = ActivityManagementActionTypes.DELETE_ACTIVITY;
1515

16-
constructor(public activityId: string) { }
16+
constructor(public activityId: string) {}
1717
}
1818

1919
export class DeleteActivitySuccess implements Action {
20-
public readonly type = ActivityManagementActionTypes.DELETE_ACTIVITY_SUCCESS;
20+
public readonly type = ActivityManagementActionTypes.DELETE_ACTIVITY_SUCCESS;
2121

22-
constructor(public activityId: string) { }
22+
constructor(public activityId: string) {}
2323
}
2424

2525
export class LoadActivities implements Action {
26-
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES;
26+
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES;
2727
}
2828

2929
export class LoadActivitiesSuccess implements Action {
30-
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS;
30+
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS;
3131

32-
constructor(public payload: Activity[]) { }
32+
constructor(public payload: Activity[]) {}
3333
}
3434

3535
export class LoadActivitiesFail implements Action {
36-
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL;
36+
public readonly type = ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL;
3737

38-
constructor(public error) { }
38+
constructor(public error) {}
3939
}
4040

41-
export type ActivityManagementActions
42-
= DeleteActivitySuccess | DeleteActivity |
43-
LoadActivities | LoadActivitiesSuccess | LoadActivitiesFail;
41+
export type ActivityManagementActions =
42+
| DeleteActivitySuccess
43+
| DeleteActivity
44+
| LoadActivities
45+
| LoadActivitiesSuccess
46+
| LoadActivitiesFail;
Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,47 @@
1-
import {Injectable} from '@angular/core';
2-
import {Actions, Effect, ofType} from '@ngrx/effects';
3-
import {Action} from '@ngrx/store';
4-
import {Observable, of} from 'rxjs';
5-
import {catchError, map, mergeMap} from 'rxjs/operators';
1+
import { Injectable } from '@angular/core';
2+
import { Actions, Effect, ofType } from '@ngrx/effects';
3+
import { Action } from '@ngrx/store';
4+
import { Observable, of } from 'rxjs';
5+
import { catchError, map, mergeMap } from 'rxjs/operators';
66

7-
import {ActivityManagementActionTypes, LoadActivitiesSuccess, LoadActivitiesFail} from './activity-management.actions';
8-
import {Activity} from './../../shared/models/activity.model';
9-
import {ActivityService} from './../services/activity.service';
7+
import {
8+
ActivityManagementActionTypes,
9+
LoadActivitiesSuccess,
10+
LoadActivitiesFail,
11+
DeleteActivitySuccess,
12+
} from './activity-management.actions';
13+
import { Activity } from './../../shared/models/activity.model';
14+
import { ActivityService } from './../services/activity.service';
1015

1116
@Injectable()
1217
export class ActivityEffects {
18+
constructor(private actions$: Actions, private activityService: ActivityService) {}
1319

14-
constructor(private actions$: Actions, private activityService: ActivityService) {}
20+
parameterFieldName = 'activityId';
1521

16-
@Effect()
17-
getActivities$: Observable < Action > = this
18-
.actions$
19-
.pipe(ofType(ActivityManagementActionTypes.LOAD_ACTIVITIES),
20-
mergeMap(() => this.activityService.getActivities().pipe(map((activities: Activity[]) => {
21-
return new LoadActivitiesSuccess(activities);
22-
}), catchError((error) => of(new LoadActivitiesFail(error))))));
22+
@Effect()
23+
getActivities$: Observable<Action> = this.actions$.pipe(
24+
ofType(ActivityManagementActionTypes.LOAD_ACTIVITIES),
25+
mergeMap(() =>
26+
this.activityService.getActivities().pipe(
27+
map((activities: Activity[]) => {
28+
return new LoadActivitiesSuccess(activities);
29+
}),
30+
catchError((error) => of(new LoadActivitiesFail(error)))
31+
)
32+
)
33+
);
2334

24-
// TODO: implement the proper code to delete an activity
25-
// @Effect()
26-
// deleteActivity$: Observable < Action > = this
27-
// .actions$
28-
// .pipe(ofType(ActivityManagementActionTypes.DELETE_ACTIVITY),
29-
// mergeMap(() => this.activityService.deleteActivity().pipe(map((activities: Activity[]) => {
30-
// return new LoadActivitiesSuccess(activities);
31-
// }), catchError((error) => of(new LoadActivitiesFail(error))))));
35+
@Effect()
36+
deleteActivity$: Observable<Action> = this.actions$.pipe(
37+
ofType(ActivityManagementActionTypes.DELETE_ACTIVITY),
38+
map((action) => action[this.parameterFieldName]),
39+
mergeMap((activityId) =>
40+
this.activityService.deleteActivity(activityId).pipe(
41+
map(() => {
42+
return new DeleteActivitySuccess(activityId);
43+
})
44+
)
45+
)
46+
);
3247
}
Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {ActivityManagementActions, ActivityManagementActionTypes} from './activity-management.actions';
2-
import {Activity} from './../../shared/models/activity.model';
1+
import { ActivityManagementActions, ActivityManagementActionTypes } from './activity-management.actions';
2+
import { Activity } from './../../shared/models/activity.model';
33

44
export interface ActivityState {
55
data: Activity[];
@@ -10,45 +10,48 @@ export interface ActivityState {
1010
const initialState: ActivityState = {
1111
data: [],
1212
isLoading: false,
13-
message: ''
13+
message: '',
1414
};
1515

16-
export function activityManagementReducer(state: ActivityState = initialState, action: ActivityManagementActions): ActivityState {
17-
16+
export function activityManagementReducer(
17+
state: ActivityState = initialState,
18+
action: ActivityManagementActions
19+
): ActivityState {
1820
switch (action.type) {
19-
case(ActivityManagementActionTypes.DELETE_ACTIVITY): {
21+
case ActivityManagementActionTypes.DELETE_ACTIVITY: {
2022
return {
2123
...state,
22-
message: 'Activity removed successfully!'
24+
message: 'Activity removed successfully!',
2325
};
2426
}
2527

26-
case(ActivityManagementActionTypes.DELETE_ACTIVITY_SUCCESS): {
28+
case ActivityManagementActionTypes.DELETE_ACTIVITY_SUCCESS: {
2729
const stateWithDeletedActivity = initialState;
28-
stateWithDeletedActivity.data = state.data.filter(activity => activity.id !== action.activityId);
30+
stateWithDeletedActivity.data = state.data.filter((activity) => activity.id !== action.activityId);
31+
console.log();
2932
return {
3033
...stateWithDeletedActivity,
31-
message: 'Activity removed successfully!'
34+
message: 'Activity removed successfully!',
3235
};
3336
}
3437

35-
case(ActivityManagementActionTypes.LOAD_ACTIVITIES): {
36-
return {
37-
...state,
38-
isLoading: true
39-
};
40-
}
38+
case ActivityManagementActionTypes.LOAD_ACTIVITIES: {
39+
return {
40+
...state,
41+
isLoading: true,
42+
};
43+
}
4144

4245
case ActivityManagementActionTypes.LOAD_ACTIVITIES_SUCCESS: {
43-
return {
44-
...state,
45-
data: action.payload,
46-
isLoading: false,
47-
message: 'Data fetch successfully!'
48-
};
49-
}
46+
return {
47+
...state,
48+
data: action.payload,
49+
isLoading: false,
50+
message: 'Data fetch successfully!',
51+
};
52+
}
5053
case ActivityManagementActionTypes.LOAD_ACTIVITIES_FAIL: {
51-
return { data: [], isLoading: false, message: 'Something went wrong fetching activities!' };
52-
}
54+
return { data: [], isLoading: false, message: 'Something went wrong fetching activities!' };
55+
}
5356
}
5457
}

0 commit comments

Comments
 (0)