Skip to content

Commit a62c980

Browse files
feat: TT-208 change ui-switch to button
1 parent e8ee50f commit a62c980

File tree

9 files changed

+169
-58
lines changed

9 files changed

+169
-58
lines changed

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<th class="col-4 text-center">Activity ID</th>
66
<th class="col-4 text-center">Activity</th>
77
<th class="col-2 text-center">Options</th>
8-
<th class="col-2 text-center">Visibility</th>
8+
<th class="col-2 text-center">Operation</th>
99
</tr>
1010
</thead>
1111
<app-loading-bar *ngIf="isLoading$ | async"></app-loading-bar>
@@ -19,14 +19,17 @@
1919
</button>
2020
</td>
2121
<td class="col-2 text-center">
22-
<ui-switch
23-
size="small"
22+
<button
23+
class="btn btn-sm"
2424
data-toggle="modal"
2525
data-target="#deleteModal"
26-
[checked]="activity.status === 'active'"
27-
(valueChange)="switchStatus($event, activity)"
28-
[beforeChange]="OnSwitch(activity)"
29-
></ui-switch>
26+
[ngClass]="activity.btnColor"
27+
(click)="changeOperation(activity)"
28+
>
29+
<i class="fa" [ngClass]="activity.btnIcon"></i>
30+
{{ activity.btnName }}
31+
<span *ngIf="activity._status">&nbsp;</span>
32+
</button>
3033
</td>
3134
</tr>
3235
</tbody>

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

Lines changed: 90 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,34 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
33

44
import { allActivities } from './../../store/activity-management.selectors';
55
import { ActivityState } from './../../store/activity-management.reducers';
6-
import { ArchiveActivity, SetActivityToEdit } from './../../store/activity-management.actions';
6+
import { ArchiveActivity, SetActivityToEdit, UnarchiveActivity } from './../../store/activity-management.actions';
77
import { ActivityListComponent } from './activity-list.component';
88

9-
fdescribe('ActivityListComponent', () => {
9+
describe('ActivityListComponent', () => {
1010
let component: ActivityListComponent;
1111
let fixture: ComponentFixture<ActivityListComponent>;
1212
let store: MockStore<ActivityState>;
1313
let mockActivitiesSelector;
1414

1515
const state = {
16-
data: [{ id: 'id', name: 'name', description: 'description', status: 'inactive' }],
16+
data: [{ id: '1', name: 'name', description: 'description', status: 'inactive' }],
1717
isLoading: false,
1818
message: '',
1919
activityIdToEdit: '',
2020
};
21+
const operationBtnProps = [{
22+
key: 'active',
23+
_status: false,
24+
btnColor: 'btn-danger',
25+
btnIcon: 'fa-arrow-circle-down',
26+
btnName: 'Archive',
27+
}, {
28+
key: 'inactive',
29+
_status: true,
30+
btnColor: 'btn-primary',
31+
btnIcon: 'fa-arrow-circle-up',
32+
btnName: 'Active',
33+
}];
2134

2235
beforeEach(waitForAsync(() => {
2336
TestBed.configureTestingModule({
@@ -48,9 +61,24 @@ fdescribe('ActivityListComponent', () => {
4861
expect(store.dispatch).toHaveBeenCalled();
4962
});
5063

64+
it('onInit, activities field is populated with data from store', () => {
65+
component.ngOnInit();
66+
67+
const expectedData = state.data.map(item => {
68+
const props = operationBtnProps.find(prop => prop.key === item.status);
69+
return { ...item, ...props };
70+
});
71+
72+
expect(component.activities).toEqual(expectedData);
73+
});
74+
75+
afterEach(() => {
76+
fixture.destroy();
77+
});
78+
5179
it('deleteActivity, dispatchs DeleteActivity action', () => {
5280
spyOn(store, 'dispatch');
53-
component.idToDelete = 'id';
81+
component.idToModify = 'id';
5482
component.deleteActivity();
5583

5684
expect(store.dispatch).toHaveBeenCalledWith(new ArchiveActivity('id'));
@@ -64,13 +92,66 @@ fdescribe('ActivityListComponent', () => {
6492
expect(store.dispatch).toHaveBeenCalledWith(new SetActivityToEdit('id'));
6593
});
6694

67-
it('onInit, activities field is populated with data from store', () => {
68-
component.ngOnInit();
95+
it('unarchiveActivity, dispatchs UnarchiveActivity action', () => {
96+
spyOn(store, 'dispatch');
97+
component.idToModify = 'id';
98+
component.unarchiveActivity();
6999

70-
expect(component.activities).toBe(state.data);
100+
expect(store.dispatch).toHaveBeenCalledWith(new UnarchiveActivity('id'));
71101
});
72102

73-
afterEach(() => {
74-
fixture.destroy();
103+
it('openModal should set on true and display \"Are you sure you want to archive activity\"', () => {
104+
const message = 'Are you sure you want to archive activity name?';
105+
const itemData = {
106+
id: '1',
107+
name: 'name',
108+
description: 'description',
109+
status: 'active',
110+
key: 'active',
111+
_status: false,
112+
btnColor: 'btn-danger',
113+
btnIcon: 'fa-arrow-circle-down',
114+
btnName: 'Archive',
115+
};
116+
117+
component.openModal(itemData);
118+
expect(component.showModal).toBeTrue();
119+
expect(component.message).toBe(message);
120+
});
121+
122+
it('changeOperation should call unarchiveActivity() on item._status = true', () => {
123+
const itemData = {
124+
id: '1',
125+
name: 'name',
126+
description: 'description',
127+
status: 'inactive',
128+
key: 'inactive',
129+
_status: true,
130+
btnColor: 'btn-primary',
131+
btnIcon: 'fa-arrow-circle-up',
132+
btnName: 'Active',
133+
};
134+
135+
spyOn(component, 'unarchiveActivity');
136+
component.changeOperation(itemData);
137+
expect(component.unarchiveActivity).toHaveBeenCalled();
138+
});
139+
140+
it('changeOperation should call openModal() on item._status = false', () => {
141+
const itemData = {
142+
id: '1',
143+
name: 'name',
144+
description: 'description',
145+
status: 'active',
146+
key: 'active',
147+
_status: false,
148+
btnColor: 'btn-danger',
149+
btnIcon: 'fa-arrow-circle-down',
150+
btnName: 'Archive',
151+
};
152+
153+
spyOn(component, 'openModal');
154+
component.changeOperation(itemData);
155+
expect(component.openModal).toHaveBeenCalled();
75156
});
76157
});
Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Component, OnInit } from '@angular/core';
22
import { select, Store } from '@ngrx/store';
3-
import { Observable, of } from 'rxjs';
4-
import { delay } from 'rxjs/operators';
3+
import { Observable } from 'rxjs';
4+
import { delay, map } from 'rxjs/operators';
55
import { getIsLoading } from 'src/app/modules/activities-management/store/activity-management.selectors';
6-
import { Activity } from '../../../shared/models';
6+
import { Activity, ActivityFront } from '../../../shared/models';
77
import { allActivities } from '../../store';
88
import { ArchiveActivity, LoadActivities, SetActivityToEdit, UnarchiveActivity } from './../../store/activity-management.actions';
99
import { ActivityState } from './../../store/activity-management.reducers';
@@ -17,51 +17,65 @@ export class ActivityListComponent implements OnInit {
1717
constructor(private store: Store<ActivityState>) {
1818
this.isLoading$ = store.pipe(delay(0), select(getIsLoading));
1919
}
20-
activities: Activity[] = [];
20+
activities: ActivityFront[] = [];
2121
showModal = false;
2222
activityToDelete: Activity;
2323
message: string;
24-
idToDelete: string;
24+
idToModify: string;
2525
isLoading$: Observable<boolean>;
2626

2727
ngOnInit() {
28+
const operationBtnProps = [{
29+
key: 'active',
30+
_status: false,
31+
btnColor: 'btn-danger',
32+
btnIcon: 'fa-arrow-circle-down',
33+
btnName: 'Archive',
34+
}, {
35+
key: 'inactive',
36+
_status: true,
37+
btnColor: 'btn-primary',
38+
btnIcon: 'fa-arrow-circle-up',
39+
btnName: 'Active',
40+
}];
41+
2842
this.store.dispatch(new LoadActivities());
29-
const activities$ = this.store.pipe(select(allActivities));
43+
const activities$ = this.store.pipe(
44+
select(allActivities),
45+
map((activity: Activity[]) => {
46+
return activity.map(item => {
47+
const addProps = operationBtnProps.find(prop => (prop.key === item.status));
48+
return { ...item, ...addProps };
49+
});
50+
}),
51+
);
3052

3153
activities$.subscribe((response) => {
3254
this.activities = response;
3355
});
3456
}
3557

36-
deleteActivity() {
37-
this.store.dispatch(new ArchiveActivity(this.idToDelete));
58+
deleteActivity(): void {
59+
this.store.dispatch(new ArchiveActivity(this.idToModify));
3860
this.showModal = false;
3961
}
4062

41-
updateActivity(activityId: string) {
63+
updateActivity(activityId: string): void {
4264
this.store.dispatch(new SetActivityToEdit(activityId));
4365
}
4466

45-
openModal(item: Activity) {
46-
this.idToDelete = item.id;
47-
this.message = `Are you sure you want to archive activity ${item.name}?`;
48-
this.showModal = true;
67+
unarchiveActivity(): void {
68+
this.store.dispatch(new UnarchiveActivity(this.idToModify));
69+
this.showModal = false;
4970
}
5071

51-
switchStatus(evt: boolean, item: Activity): void {
52-
if (!evt) {
53-
// FIXME: Si el usuario selecciona cancelar, se produce la animación de cambio
54-
// del switch
55-
this.openModal(item);
56-
} else {
57-
this.showModal = false;
58-
this.store.dispatch(new UnarchiveActivity(item.id));
59-
}
72+
openModal(item: Activity): void {
73+
this.message = `Are you sure you want to archive activity ${item.name}?`;
74+
this.showModal = true;
6075
}
6176

62-
OnSwitch(item: Activity): Observable<boolean> {
63-
// FIXME: OnBeforeChange debería ser un evento que traiga el estado del switch
64-
// para poder realizar una acción antes de que el estado cambie.
65-
return of(true);
77+
changeOperation(item: ActivityFront): void {
78+
this.idToModify = item.id;
79+
!item._status ? this.openModal(item) : this.unarchiveActivity();
6680
}
6781
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@ describe('LoadActivitiesSuccess', () => {
2525
expect(createActivityFail.type).toEqual(actions.ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL);
2626
});
2727

28-
fit('ArchiveActivity type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY', () => {
28+
it('ArchiveActivity type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY', () => {
2929
const archiveActivity = new actions.ArchiveActivity('id_test');
3030
expect(archiveActivity.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY);
3131
});
3232

33-
fit('ArchiveActivitySuccess type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_SUCCESS', () => {
33+
it('ArchiveActivitySuccess type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_SUCCESS', () => {
3434
const archiveActivitySuccess = new actions.ArchiveActivitySuccess({
3535
id: 'id_test',
3636
status: 'inactive'
3737
});
3838
expect(archiveActivitySuccess.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY_SUCCESS);
3939
});
4040

41-
fit('ArchiveActivityFail type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_FAIL', () => {
41+
it('ArchiveActivityFail type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_FAIL', () => {
4242
const archiveActivityFail = new actions.ArchiveActivityFail('error');
4343
expect(archiveActivityFail.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY_FAIL);
4444
});
@@ -57,20 +57,20 @@ describe('LoadActivitiesSuccess', () => {
5757
expect(updateActivityFail.type).toEqual(actions.ActivityManagementActionTypes.UPDATE_ACTIVITY_FAIL);
5858
});
5959

60-
fit('UnarchiveActivity type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY', () => {
60+
it('UnarchiveActivity type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY', () => {
6161
const unarchiveActivity = new actions.UnarchiveActivity('id_test');
6262
expect(unarchiveActivity.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY);
6363
});
6464

65-
fit('UnarchiveActivitySuccess type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_SUCCESS', () => {
65+
it('UnarchiveActivitySuccess type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_SUCCESS', () => {
6666
const unarchiveActivitySuccess = new actions.UnarchiveActivitySuccess({
6767
id: 'id_test',
6868
status: 'active'
6969
});
7070
expect(unarchiveActivitySuccess.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_SUCCESS);
7171
});
7272

73-
fit('UnarchiveActivityFail type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_FAIL', () => {
73+
it('UnarchiveActivityFail type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_FAIL', () => {
7474
const unarchiveActivityFail = new actions.UnarchiveActivityFail('error');
7575
expect(unarchiveActivityFail.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_FAIL);
7676
});

src/app/modules/activities-management/store/activity-management.effects.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('ActivityEffects', () => {
7777
});
7878
});
7979

80-
fit('action type is UNARCHIVE_ACTIVITY_SUCCESS when service is executed sucessfully', async () => {
80+
it('action type is UNARCHIVE_ACTIVITY_SUCCESS when service is executed sucessfully', async () => {
8181
const activityId = 'activityId';
8282
actions$ = of({ type: ActivityManagementActionTypes.UNARCHIVE_ACTIVITY, activityId });
8383
spyOn(service, 'updateActivity').and.returnValue(of(activity));
@@ -89,7 +89,7 @@ describe('ActivityEffects', () => {
8989
});
9090
});
9191

92-
fit('action type is UNARCHIVE_ACTIVITY_FAIL when service fail in execution', async () => {
92+
it('action type is UNARCHIVE_ACTIVITY_FAIL when service fail in execution', async () => {
9393
actions$ = of({ type: ActivityManagementActionTypes.UNARCHIVE_ACTIVITY, activity });
9494
spyOn(service, 'updateActivity').and.returnValue(throwError({ error: { message: 'fail!' } }));
9595
spyOn(toastrService, 'error');
@@ -122,7 +122,7 @@ describe('ActivityEffects', () => {
122122
});
123123
});
124124

125-
fit('action type is ARCHIVE_ACTIVITY_SUCCESS when service is executed sucessfully', async () => {
125+
it('action type is ARCHIVE_ACTIVITY_SUCCESS when service is executed sucessfully', async () => {
126126
const activityId = 'activityId';
127127
actions$ = of({ type: ActivityManagementActionTypes.ARCHIVE_ACTIVITY, activityId });
128128
spyOn(service, 'deleteActivity').and.returnValue(of({}));

src/app/modules/activities-management/store/activity-management.reducers.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,15 @@ describe('activityManagementReducer', () => {
5656
expect(state.isLoading).toEqual(false);
5757
});
5858

59-
fit('on ArchiveActivity, isLoading is true', () => {
59+
it('on ArchiveActivity, isLoading is true', () => {
6060
const activityToDeleteId = '1';
6161
const action = new actions.ArchiveActivity(activityToDeleteId);
6262

6363
const state = activityManagementReducer(initialState, action);
6464
expect(state.isLoading).toBeTrue();
6565
});
6666

67-
fit('on ArchiveActivitySuccess, message equal to Activity archived successfully!', () => {
67+
it('on ArchiveActivitySuccess, message equal to Activity archived successfully!', () => {
6868
const currentActivity = { ...activity };
6969
currentActivity.status = 'active';
7070
const currentState: ActivityState = { data: [currentActivity], isLoading: false, message: '', activityIdToEdit: '' };
@@ -115,15 +115,15 @@ describe('activityManagementReducer', () => {
115115
});
116116

117117

118-
fit('on UnarchiveActivity, isLoading is true', () => {
118+
it('on UnarchiveActivity, isLoading is true', () => {
119119
const action = new actions.UnarchiveActivity('id_test');
120120

121121
const state = activityManagementReducer(initialState, action);
122122

123123
expect(state.isLoading).toBeTrue();
124124
});
125125

126-
fit('on UnarchiveActivitySuccess, status activity is change to \"active\" in the store', () => {
126+
it('on UnarchiveActivitySuccess, status activity is change to \"active\" in the store', () => {
127127
const currentState: ActivityState = { data: [activity], isLoading: false, message: '', activityIdToEdit: '1' };
128128
const activityEdited: Status = { id: '1', status: 'active' };
129129
const expectedActivity: Activity = { id: '1', name: 'Training', description: 'It is good for learning', status: 'active' };
@@ -134,7 +134,7 @@ describe('activityManagementReducer', () => {
134134
expect(state.isLoading).toBeFalse();
135135
});
136136

137-
fit('on UnarchiveActivityFail, message equal to \"Something went wrong unarchiving activities!\"', () => {
137+
it('on UnarchiveActivityFail, message equal to \"Something went wrong unarchiving activities!\"', () => {
138138
const action = new actions.UnarchiveActivityFail('error');
139139

140140
const state = activityManagementReducer(initialState, action);

src/app/modules/activities-management/store/activity-management.selectors.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('ActivityManagement Selectors', () => {
2424
expect(selectors.allActivities.projector(activityState)).toBe(activities);
2525
});
2626

27-
fit('should return all active data in the state when the selector allActiveActivities is called', () => {
27+
it('should return all active data in the state when the selector allActiveActivities is called', () => {
2828
const activities = [{ id: 'id', name: 'abc', description: 'xxx', status: 'active' },
2929
{ id: '2', name: 'xyz', description: 'yyy', status: 'inactive' },
3030
{ id: '3', name: 'xyzw', description: 'www', status: 'active' }];

0 commit comments

Comments
 (0)