Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
217fbb8
feat: TT-208 add new column in activity-list
thegreatyamori Apr 19, 2021
6c1c06f
feat: TT-208 add switch status button in column
thegreatyamori Apr 19, 2021
dc10fcf
feat: TT-208 remove delete button & assign openmodal to switch
thegreatyamori Apr 20, 2021
fb37b3b
feat: TT-208 connect switch btn with ngrx flux
thegreatyamori Apr 20, 2021
f0275f8
feat: TT-208 update ngrx delete flux
thegreatyamori Apr 20, 2021
e8ee50f
feat: TT-208 show active activities in entry & details fields
thegreatyamori Apr 21, 2021
a62c980
feat: TT-208 change ui-switch to button
thegreatyamori Apr 22, 2021
5c19c86
fix: TT-208 display the required activities when clicking on time en…
thegreatyamori Apr 26, 2021
a76051c
Merge branch 'master' into TT-208-don't-allow-deleting-activities
thegreatyamori Apr 26, 2021
aa698ee
feat: TT-208 add new column in activity-list
thegreatyamori Apr 26, 2021
5ca9a8f
feat: TT-208 add switch status button in column
thegreatyamori Apr 26, 2021
17d5acd
feat: TT-208 remove delete button & assign openmodal to switch
thegreatyamori Apr 26, 2021
90742ad
feat: TT-208 connect switch btn with ngrx flux
thegreatyamori Apr 26, 2021
4977dff
feat: TT-208 update ngrx delete flux
thegreatyamori Apr 26, 2021
1d0107d
feat: TT-208 show active activities in entry & details fields
thegreatyamori Apr 21, 2021
5808250
feat: TT-208 change ui-switch to button
thegreatyamori Apr 22, 2021
286033c
fix: TT-208 display the required activities when clicking on time en…
thegreatyamori Apr 26, 2021
3a5e107
feat: TT-208 rebase on latest master commit
thegreatyamori Apr 26, 2021
1c9ee96
fix: TT-208 merging conflicts
thegreatyamori Apr 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,32 @@
<thead class="thead-blue">
<tr class="d-flex">
<th class="col-4 text-center">Activity ID</th>
<th class="col-5 text-center">Activity</th>
<th class="col-3 text-center">Options</th>
<th class="col-4 text-center">Activity</th>
<th class="col-2 text-center">Options</th>
<th class="col-2 text-center">Operation</th>
</tr>
</thead>
<app-loading-bar *ngIf="(isLoading$ | async)"></app-loading-bar>
<app-loading-bar *ngIf="isLoading$ | async"></app-loading-bar>
<tbody *ngIf="(isLoading$ | async) === false">
<tr class="d-flex" *ngFor="let activity of activities">
<td class="col-4 text-break">{{ activity.id }}</td>
<td class="col-5 ">{{ activity.name }}</td>
<td class="col-3 text-center">
<button
type="button"
class="btn btn-sm btn-primary"
(click)="updateActivity(activity.id)"
>
<td class="col-4">{{ activity.name }}</td>
<td class="col-2 text-center">
<button type="button" class="btn btn-sm btn-primary" (click)="updateActivity(activity.id)">
<i class="fa fa-pencil fa-xs"></i>
</button>
</td>
<td class="col-2 text-center">
<button
type="button"
class="btn btn-sm btn-danger ml-2"
class="btn btn-sm"
data-toggle="modal"
data-target="#deleteModal"
(click)="openModal(activity)"
[ngClass]="activity.btnColor"
(click)="changeOperation(activity)"
>
<i class="fas fa-trash-alt fa-xs"></i>
<i class="fa" [ngClass]="activity.btnIcon"></i>
{{ activity.btnName }}
<span *ngIf="activity._status">&nbsp;</span>
</button>
</td>
</tr>
Expand All @@ -42,7 +43,7 @@
tabindex="-1"
role="dialog"
aria-hidden="true"
[title]="'Delete Activity'"
[title]="'Archive Activity'"
[body]="message"
(closeModalEvent)="deleteActivity()"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';

import { allActivities } from './../../store/activity-management.selectors';
import { ActivityState } from './../../store/activity-management.reducers';
import { DeleteActivity, SetActivityToEdit } from './../../store/activity-management.actions';
import { ArchiveActivity, SetActivityToEdit, UnarchiveActivity } from './../../store/activity-management.actions';
import { ActivityListComponent } from './activity-list.component';

describe('ActivityListComponent', () => {
Expand All @@ -13,11 +13,24 @@ describe('ActivityListComponent', () => {
let mockActivitiesSelector;

const state = {
data: [{ id: 'id', name: 'name', description: 'description' }],
data: [{ id: '1', name: 'name', description: 'description', status: 'inactive' }],
isLoading: false,
message: '',
activityIdToEdit: '',
};
const operationBtnProps = [{
key: 'active',
_status: false,
btnColor: 'btn-danger',
btnIcon: 'fa-arrow-circle-down',
btnName: 'Archive',
}, {
key: 'inactive',
_status: true,
btnColor: 'btn-primary',
btnIcon: 'fa-arrow-circle-up',
btnName: 'Active',
}];

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
Expand Down Expand Up @@ -48,12 +61,27 @@ describe('ActivityListComponent', () => {
expect(store.dispatch).toHaveBeenCalled();
});

it('onInit, activities field is populated with data from store', () => {
component.ngOnInit();

const expectedData = state.data.map(item => {
const props = operationBtnProps.find(prop => prop.key === item.status);
return { ...item, ...props };
});

expect(component.activities).toEqual(expectedData);
});

afterEach(() => {
fixture.destroy();
});

it('deleteActivity, dispatchs DeleteActivity action', () => {
spyOn(store, 'dispatch');
component.idToDelete = 'id';
component.idToModify = 'id';
component.deleteActivity();

expect(store.dispatch).toHaveBeenCalledWith(new DeleteActivity('id'));
expect(store.dispatch).toHaveBeenCalledWith(new ArchiveActivity('id'));
});

it('updateActivity, dispatchs SetActivityToEdit action', () => {
Expand All @@ -64,13 +92,66 @@ describe('ActivityListComponent', () => {
expect(store.dispatch).toHaveBeenCalledWith(new SetActivityToEdit('id'));
});

it('onInit, activities field is populated with data from store', () => {
component.ngOnInit();
it('unarchiveActivity, dispatchs UnarchiveActivity action', () => {
spyOn(store, 'dispatch');
component.idToModify = 'id';
component.unarchiveActivity();

expect(component.activities).toBe(state.data);
expect(store.dispatch).toHaveBeenCalledWith(new UnarchiveActivity('id'));
});

afterEach(() => {
fixture.destroy();
it('openModal should set on true and display \"Are you sure you want to archive activity\"', () => {
const message = 'Are you sure you want to archive activity name?';
const itemData = {
id: '1',
name: 'name',
description: 'description',
status: 'active',
key: 'active',
_status: false,
btnColor: 'btn-danger',
btnIcon: 'fa-arrow-circle-down',
btnName: 'Archive',
};

component.openModal(itemData);
expect(component.showModal).toBeTrue();
expect(component.message).toBe(message);
});

it('changeOperation should call unarchiveActivity() on item._status = true', () => {
const itemData = {
id: '1',
name: 'name',
description: 'description',
status: 'inactive',
key: 'inactive',
_status: true,
btnColor: 'btn-primary',
btnIcon: 'fa-arrow-circle-up',
btnName: 'Active',
};

spyOn(component, 'unarchiveActivity');
component.changeOperation(itemData);
expect(component.unarchiveActivity).toHaveBeenCalled();
});

it('changeOperation should call openModal() on item._status = false', () => {
const itemData = {
id: '1',
name: 'name',
description: 'description',
status: 'active',
key: 'active',
_status: false,
btnColor: 'btn-danger',
btnIcon: 'fa-arrow-circle-down',
btnName: 'Archive',
};

spyOn(component, 'openModal');
component.changeOperation(itemData);
expect(component.openModal).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -1,51 +1,81 @@
import { Component, OnInit } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { delay } from 'rxjs/operators';
import { delay, map } from 'rxjs/operators';
import { getIsLoading } from 'src/app/modules/activities-management/store/activity-management.selectors';
import { Activity } from '../../../shared/models';
import { Activity, ActivityFront } from '../../../shared/models';
import { allActivities } from '../../store';
import { DeleteActivity, LoadActivities, SetActivityToEdit } from './../../store/activity-management.actions';
import { ArchiveActivity, LoadActivities, SetActivityToEdit, UnarchiveActivity } from './../../store/activity-management.actions';
import { ActivityState } from './../../store/activity-management.reducers';


@Component({
selector: 'app-activity-list',
templateUrl: './activity-list.component.html',
styleUrls: ['./activity-list.component.scss'],
})
export class ActivityListComponent implements OnInit {
activities: Activity[] = [];
constructor(private store: Store<ActivityState>) {
this.isLoading$ = store.pipe(delay(0), select(getIsLoading));
}
activities: ActivityFront[] = [];
showModal = false;
activityToDelete: Activity;
message: string;
idToDelete: string;
idToModify: string;
isLoading$: Observable<boolean>;
constructor(private store: Store<ActivityState>) {
this.isLoading$ = store.pipe(delay(0), select(getIsLoading));
}

ngOnInit() {
const operationBtnProps = [{
key: 'active',
_status: false,
btnColor: 'btn-danger',
btnIcon: 'fa-arrow-circle-down',
btnName: 'Archive',
}, {
key: 'inactive',
_status: true,
btnColor: 'btn-primary',
btnIcon: 'fa-arrow-circle-up',
btnName: 'Active',
}];

this.store.dispatch(new LoadActivities());
const activities$ = this.store.pipe(select(allActivities));
const activities$ = this.store.pipe(
select(allActivities),
map((activity: Activity[]) => {
return activity.map(item => {
const addProps = operationBtnProps.find(prop => (prop.key === item.status));
return { ...item, ...addProps };
});
}),
);

activities$.subscribe((response) => {
this.activities = response;
});
}

deleteActivity() {
this.store.dispatch(new DeleteActivity(this.idToDelete));
this.showModal = true;
deleteActivity(): void {
this.store.dispatch(new ArchiveActivity(this.idToModify));
this.showModal = false;
}

updateActivity(activityId: string) {
updateActivity(activityId: string): void {
this.store.dispatch(new SetActivityToEdit(activityId));
}

openModal(item: Activity) {
this.idToDelete = item.id;
this.message = `Are you sure you want to delete ${item.name}?`;
unarchiveActivity(): void {
this.store.dispatch(new UnarchiveActivity(this.idToModify));
this.showModal = false;
}

openModal(item: Activity): void {
this.message = `Are you sure you want to archive activity ${item.name}?`;
this.showModal = true;
}

changeOperation(item: ActivityFront): void {
this.idToModify = item.id;
!item._status ? this.openModal(item) : this.unarchiveActivity();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,24 @@ describe('LoadActivitiesSuccess', () => {
expect(createActivityFail.type).toEqual(actions.ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL);
});

it('ArchiveActivity type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY', () => {
const archiveActivity = new actions.ArchiveActivity('id_test');
expect(archiveActivity.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY);
});

it('ArchiveActivitySuccess type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_SUCCESS', () => {
const archiveActivitySuccess = new actions.ArchiveActivitySuccess({
id: 'id_test',
status: 'inactive'
});
expect(archiveActivitySuccess.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY_SUCCESS);
});

it('ArchiveActivityFail type is ActivityManagementActionTypes.ARCHIVE_ACTIVITY_FAIL', () => {
const archiveActivityFail = new actions.ArchiveActivityFail('error');
expect(archiveActivityFail.type).toEqual(actions.ActivityManagementActionTypes.ARCHIVE_ACTIVITY_FAIL);
});

it('UpdateActivitySuccess type is ActivityManagementActionTypes.UPDATE_ACTIVITY_SUCCESS', () => {
const updateActivitySuccess = new actions.UpdateActivitySuccess({
id: '1',
Expand All @@ -39,6 +57,24 @@ describe('LoadActivitiesSuccess', () => {
expect(updateActivityFail.type).toEqual(actions.ActivityManagementActionTypes.UPDATE_ACTIVITY_FAIL);
});

it('UnarchiveActivity type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY', () => {
const unarchiveActivity = new actions.UnarchiveActivity('id_test');
expect(unarchiveActivity.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY);
});

it('UnarchiveActivitySuccess type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_SUCCESS', () => {
const unarchiveActivitySuccess = new actions.UnarchiveActivitySuccess({
id: 'id_test',
status: 'active'
});
expect(unarchiveActivitySuccess.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_SUCCESS);
});

it('UnarchiveActivityFail type is ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_FAIL', () => {
const unarchiveActivityFail = new actions.UnarchiveActivityFail('error');
expect(unarchiveActivityFail.type).toEqual(actions.ActivityManagementActionTypes.UNARCHIVE_ACTIVITY_FAIL);
});

it('SetActivityToEdit type is ActivityManagementActionTypes.SET_ACTIVITY_ID_TO_EDIT', () => {
const setActivityToEdit = new actions.SetActivityToEdit('123');
expect(setActivityToEdit.type).toEqual(actions.ActivityManagementActionTypes.SET_ACTIVITY_ID_TO_EDIT);
Expand Down
Loading