Skip to content

Commit 8bc8018

Browse files
authored
Merge pull request #102 from ioet/68-update-activities
#68 update activity
2 parents fbcf2a3 + 1ae4bde commit 8bc8018

15 files changed

+415
-90
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
@@ -9,7 +9,7 @@ <h2 class="mb-0">
99
{{ activity.name }}
1010
</a>
1111
<div class="btn-group float-right" role="group">
12-
<i class="far fa-edit btn btn-link text-white"></i>
12+
<i class="far fa-edit btn btn-link text-white" (click)="updateActivity(activity.id)"></i>
1313
<i (click)="deleteActivity(activity.id)" class="far fa-trash-alt btn btn-link text-white"></i>
1414
</div>
1515
</h2>

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,31 @@ import { ActivityListComponent } from './activity-list.component';
88
describe('ActivityListComponent', () => {
99
let component: ActivityListComponent;
1010
let fixture: ComponentFixture<ActivityListComponent>;
11+
let store: MockStore<ActivityState>;
1112
let mockActivitiesSelector;
1213

13-
const state = { data: [{id: 'id', name: 'name', description: 'description'}], isLoading: false, message: '' };
14-
15-
let store: MockStore<ActivityState>;
14+
const state = {
15+
data: [{ id: 'id', name: 'name', description: 'description' }],
16+
isLoading: false,
17+
message: '',
18+
activityIdToEdit: '',
19+
};
1620

1721
beforeEach(async(() => {
1822
TestBed.configureTestingModule({
19-
declarations: [ ActivityListComponent ],
20-
providers: [ provideMockStore({ initialState: state }) ]
21-
})
22-
.compileComponents();
23-
24-
store = TestBed.inject(MockStore);
25-
26-
mockActivitiesSelector = store.overrideSelector( allActivities, state );
23+
declarations: [ActivityListComponent],
24+
providers: [provideMockStore({ initialState: state })],
25+
}).compileComponents();
2726
}));
2827

2928
beforeEach(() => {
3029
fixture = TestBed.createComponent(ActivityListComponent);
3130
component = fixture.componentInstance;
3231
fixture.detectChanges();
32+
33+
store = TestBed.inject(MockStore);
34+
store.setState(state);
35+
mockActivitiesSelector = store.overrideSelector(allActivities, state.data);
3336
});
3437

3538
it('should create', () => {
@@ -50,6 +53,7 @@ describe('ActivityListComponent', () => {
5053
expect(component.activities).toBe(state.data);
5154
});
5255

53-
afterEach(() => { fixture.destroy(); });
54-
56+
afterEach(() => {
57+
fixture.destroy();
58+
});
5559
});

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { OnInit } from '@angular/core';
22
import { Component } from '@angular/core';
33
import { Store, select } from '@ngrx/store';
44

5-
import { LoadActivities, DeleteActivity } from './../../store/activity-management.actions';
5+
import { LoadActivities, DeleteActivity, SetActivityToEdit } from './../../store/activity-management.actions';
66
import { ActivityState } from './../../store/activity-management.reducers';
77
import { allActivities } from '../../store';
88
import { Activity } from '../../../shared/models';
@@ -14,7 +14,6 @@ import { Activity } from '../../../shared/models';
1414
})
1515
export class ActivityListComponent implements OnInit {
1616
activities: Activity[] = [];
17-
public isLoading: boolean;
1817

1918
constructor(private store: Store<ActivityState>) {}
2019

@@ -23,12 +22,15 @@ export class ActivityListComponent implements OnInit {
2322
const activities$ = this.store.pipe(select(allActivities));
2423

2524
activities$.subscribe((response) => {
26-
this.isLoading = response.isLoading;
27-
this.activities = response.data;
25+
this.activities = response;
2826
});
2927
}
3028

3129
deleteActivity(activityId: string) {
3230
this.store.dispatch(new DeleteActivity(activityId));
3331
}
32+
33+
updateActivity(activityId: string) {
34+
this.store.dispatch(new SetActivityToEdit(activityId));
35+
}
3436
}

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@
33
<h1 class="card-title">Activity</h1>
44
<div class="form-group">
55
<label for="name">Name:</label>
6-
<input class="form-control" id="name" type="text" formControlName="name"
7-
[class.is-invalid]="name.invalid && name.touched" required />
6+
<input
7+
class="form-control"
8+
id="name"
9+
type="text"
10+
formControlName="name"
11+
[class.is-invalid]="name.invalid && name.touched"
12+
required
13+
/>
814
<div class="text-danger" *ngIf="(name.dirty || name.touched) && name.invalid && name.errors.required">
915
Activity name is required.
1016
</div>
@@ -16,7 +22,14 @@ <h1 class="card-title">Activity</h1>
1622
</div>
1723

1824
<div class="btn-toolbar" role="toolbar">
19-
<button class="btn btn-primary" type="submit" [disabled]="!activityForm.valid">Save</button>
25+
<div class="btn-group mr-2" role="group">
26+
<button class="btn btn-primary mb-2" type="submit" [disabled]="!activityForm.valid">Save</button>
27+
</div>
28+
<div class="btn-group mr-2" role="group">
29+
<button class="btn btn-secondary mb-2" type="reset" [hidden]="!activityToEdit" (click)="cancelButton()">
30+
Cancel
31+
</button>
32+
</div>
2033
</div>
2134
</form>
22-
</div>
35+
</div>
Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +0,0 @@
1-
@import '../../../../../styles/colors.scss';
2-
3-
.save-button-style {
4-
background-image: $button-primary-color;
5-
color: white;
6-
}
7-
8-
.cancel-button-style {
9-
background-color: $modal-button-secondary;
10-
color: white;
11-
}

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

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,30 @@ import { async, TestBed, ComponentFixture } from '@angular/core/testing';
33
import { provideMockStore, MockStore } from '@ngrx/store/testing';
44

55
import { CreateActivityComponent } from './create-activity.component';
6-
import { ActivityState, CreateActivity } from '../../store';
6+
import {
7+
ActivityState,
8+
CreateActivity,
9+
UpdateActivity,
10+
activityIdtoEdit,
11+
allActivities,
12+
ResetActivityToEdit,
13+
} from '../../store';
714
import { Activity } from 'src/app/modules/shared/models';
815

916
describe('CreateActivityComponent', () => {
1017
let component: CreateActivityComponent;
1118
let fixture: ComponentFixture<CreateActivityComponent>;
1219
let store: MockStore<ActivityState>;
13-
14-
const state = { data: [{ id: 'id', name: 'name', description: 'description' }], isLoading: false, message: '' };
15-
16-
const data: Activity[] = [
17-
{
18-
id: '1',
19-
name: 'Training',
20-
description: 'It is good for learning',
21-
},
22-
];
20+
let activityIdtoEditMock;
21+
let allActivitiesMock;
22+
let getActivityByIdMock;
23+
24+
const state = {
25+
data: [{ id: '', name: '', description: '' }],
26+
isLoading: false,
27+
message: '',
28+
activityIdToEdit: '',
29+
};
2330

2431
const activity: Activity = {
2532
id: '1',
@@ -43,16 +50,60 @@ describe('CreateActivityComponent', () => {
4350
store.setState(state);
4451
});
4552

53+
afterEach(() => {
54+
fixture.destroy();
55+
});
56+
4657
it('should create the component', () => {
4758
expect(component).toBeTruthy();
4859
});
4960

50-
it('should reset form onSubmit', () => {
61+
it('should reset form onSubmit and dispatch UpdateActivity action', () => {
62+
const currentState = {
63+
data: [{ id: '1', name: 'xxx', description: 'xxxx' }],
64+
isLoading: false,
65+
message: '',
66+
activityIdToEdit: '1',
67+
};
68+
69+
activityIdtoEditMock = store.overrideSelector(activityIdtoEdit, currentState.activityIdToEdit);
70+
allActivitiesMock = store.overrideSelector(allActivities, currentState.data);
71+
getActivityByIdMock = store.overrideSelector(allActivitiesMock, activityIdtoEditMock);
72+
73+
component.activityToEdit = getActivityByIdMock;
74+
75+
const activityForm = {
76+
name: 'Develop',
77+
description: 'xxx',
78+
};
79+
80+
const activityUpdated = {
81+
id: component.activityToEdit.id,
82+
name: 'Develop',
83+
description: 'xxx',
84+
};
85+
5186
spyOn(component.activityForm, 'reset');
87+
spyOn(store, 'dispatch');
5288

53-
component.onSubmit(data);
89+
component.onSubmit(activityForm);
5490

5591
expect(component.activityForm.reset).toHaveBeenCalled();
92+
expect(store.dispatch).toHaveBeenCalledTimes(1);
93+
expect(store.dispatch).toHaveBeenCalledWith(new UpdateActivity(activityUpdated));
94+
});
95+
96+
it('should reset form onSubmit and dispatch CreateActivity action', () => {
97+
component.activityToEdit = undefined;
98+
99+
spyOn(component.activityForm, 'reset');
100+
spyOn(store, 'dispatch');
101+
102+
component.onSubmit(activity);
103+
104+
expect(component.activityForm.reset).toHaveBeenCalled();
105+
expect(store.dispatch).toHaveBeenCalledTimes(1);
106+
expect(store.dispatch).toHaveBeenCalledWith(new CreateActivity(activity));
56107
});
57108

58109
it('should get name using activityForm', () => {
@@ -71,10 +122,25 @@ describe('CreateActivityComponent', () => {
71122
expect(component.activityForm.get).toHaveBeenCalledWith('description');
72123
});
73124

74-
it('should dispatch createActivity action #onSubmit', () => {
125+
it('should set data in activityForm', () => {
126+
const activityDataForm = {
127+
name: 'Training',
128+
description: 'It is good for learning',
129+
};
130+
131+
spyOn(component.activityForm, 'setValue');
132+
133+
component.setDataToUpdate(activity);
134+
expect(component.activityForm.setValue).toHaveBeenCalledTimes(1);
135+
expect(component.activityForm.setValue).toHaveBeenCalledWith(activityDataForm);
136+
});
137+
138+
it('should dispatch a ResetActivityToEdit action', () => {
75139
spyOn(store, 'dispatch');
76-
component.onSubmit(activity);
140+
141+
component.cancelButton();
142+
77143
expect(store.dispatch).toHaveBeenCalledTimes(1);
78-
expect(store.dispatch).toHaveBeenCalledWith(new CreateActivity(activity));
144+
expect(store.dispatch).toHaveBeenCalledWith(new ResetActivityToEdit());
79145
});
80146
});
Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
2-
import { Component } from '@angular/core';
3-
import { Store } from '@ngrx/store';
2+
import { Component, OnInit } from '@angular/core';
3+
import { Store, select } from '@ngrx/store';
4+
5+
import { Activity } from '../../../shared/models';
46
import { ActivityState } from './../../store/activity-management.reducers';
5-
import { CreateActivity } from '../../store';
7+
import { CreateActivity, UpdateActivity, getActivityById, ResetActivityToEdit } from '../../store';
68

79
@Component({
810
selector: 'app-create-activity',
911
templateUrl: './create-activity.component.html',
1012
styleUrls: ['./create-activity.component.scss'],
1113
})
12-
export class CreateActivityComponent {
14+
export class CreateActivityComponent implements OnInit {
1315
activityForm: FormGroup;
14-
isLoading: boolean;
16+
activityToEdit: Activity;
1517

1618
constructor(private formBuilder: FormBuilder, private store: Store<ActivityState>) {
1719
this.activityForm = this.formBuilder.group({
@@ -20,9 +22,12 @@ export class CreateActivityComponent {
2022
});
2123
}
2224

23-
onSubmit(activityData) {
24-
this.activityForm.reset();
25-
this.store.dispatch(new CreateActivity(activityData));
25+
ngOnInit() {
26+
const activities$ = this.store.pipe(select(getActivityById));
27+
activities$.subscribe((activity) => {
28+
this.activityToEdit = activity;
29+
this.setDataToUpdate(this.activityToEdit);
30+
});
2631
}
2732

2833
get name() {
@@ -32,4 +37,31 @@ export class CreateActivityComponent {
3237
get description() {
3338
return this.activityForm.get('description');
3439
}
40+
41+
setDataToUpdate(activityData: Activity) {
42+
if (activityData) {
43+
this.activityForm.setValue({
44+
name: activityData.name,
45+
description: activityData.description,
46+
});
47+
}
48+
}
49+
50+
onSubmit(activityData) {
51+
this.activityForm.reset();
52+
53+
if (this.activityToEdit) {
54+
const activity = {
55+
...activityData,
56+
id: this.activityToEdit.id,
57+
};
58+
this.store.dispatch(new UpdateActivity(activity));
59+
} else {
60+
this.store.dispatch(new CreateActivity(activityData));
61+
}
62+
}
63+
64+
cancelButton() {
65+
this.store.dispatch(new ResetActivityToEdit());
66+
}
3567
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,17 @@ describe('Activity Service', () => {
6464
expect(getActivitiesRequest.request.method).toBe('DELETE');
6565
getActivitiesRequest.flush(activities);
6666
});
67+
68+
it('update activity using PUT from baseUrl', () => {
69+
const activity = { id: '1', name: 'aaa', description: 'bbb' };
70+
71+
service.baseUrl = 'activities' + '/' + activity.id;
72+
73+
service.updateActivity(activity).subscribe((response) => {
74+
expect(response.name).toBe('aaa');
75+
});
76+
const updateActivitiesRequest = httpMock.expectOne(`${service.baseUrl}/${activity.id}`);
77+
expect(updateActivitiesRequest.request.method).toBe('PUT');
78+
updateActivitiesRequest.flush(activity);
79+
});
6780
});

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,15 @@ export class ActivityService {
3030
const url = `${this.baseUrl}/${acitivityId}`;
3131
return this.http.delete(url);
3232
}
33+
34+
updateActivity(activityData): Observable<any> {
35+
const url = `${this.baseUrl}/${activityData.id}`;
36+
37+
const body = {
38+
...activityData,
39+
tenant_id: '4225ab1e-1033-4a5f-8650-0dd4950f38c8',
40+
};
41+
42+
return this.http.put(url, body);
43+
}
3344
}

0 commit comments

Comments
 (0)