Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Next Next commit
feat: #201 Adding clock-in and clock-out notifications
  • Loading branch information
Juan Gabriel Guzman committed May 5, 2020
commit 7a1bfa5efe53bb62e430e8529eb7f8bac4db11dd
5 changes: 1 addition & 4 deletions src/app/modules/time-clock/pages/time-clock.component.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
<app-notification [notificationMsg]="message | async" [isError]="isError" *ngIf="showNotification"></app-notification>
<div class="text-center mt-3">
<div class="alert alert-danger" role="alert">
{{message}}
</div>

<div class="card">
<div class="card-body">
<h6 class="text-left"><strong>Summary</strong></h6>
Expand Down
27 changes: 25 additions & 2 deletions src/app/modules/time-clock/pages/time-clock.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StopTimeEntryRunning } from './../store/entry.actions';
import {EntryActionTypes, StopTimeEntryRunning } from './../store/entry.actions';
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { provideMockStore, MockStore } from '@ngrx/store/testing';
Expand All @@ -9,13 +9,15 @@ import { ProjectListHoverComponent } from '../components';
import { ProjectService } from '../../customer-management/components/projects/components/services/project.service';
import { FilterProjectPipe } from '../../shared/pipes';
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
import {ActionsSubject} from '@ngrx/store';

describe('TimeClockComponent', () => {
let component: TimeClockComponent;
let fixture: ComponentFixture<TimeClockComponent>;
let store: MockStore<ProjectState>;
let projectService: ProjectService;
let azureAdB2CService: AzureAdB2CService;
const actionSub: ActionsSubject = new ActionsSubject();
const state = {
projects: {
projects: [{ id: 'id', name: 'name', project_type_id: '' }],
Expand Down Expand Up @@ -44,7 +46,10 @@ describe('TimeClockComponent', () => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
declarations: [TimeClockComponent, ProjectListHoverComponent, FilterProjectPipe],
providers: [ProjectService, AzureAdB2CService, provideMockStore({ initialState: state })],
providers: [ProjectService,
AzureAdB2CService,
{ provide: ActionsSubject, useValue: actionSub },
provideMockStore({ initialState: state })],
}).compileComponents();
store = TestBed.inject(MockStore);
}));
Expand Down Expand Up @@ -92,4 +97,22 @@ describe('TimeClockComponent', () => {
expect(store.dispatch).toHaveBeenCalledWith(new StopTimeEntryRunning('id'));
});

it('on success create entry, the notification is shown', () => {
const actionSubject = TestBed.get(ActionsSubject) as ActionsSubject;
const action = {
type: EntryActionTypes.CREATE_ENTRY_SUCCESS
};
actionSubject.next(action);
expect(component.showNotification).toEqual(true);
});

it('on success stop entry, the notification is shown', () => {
const actionSubject = TestBed.get(ActionsSubject) as ActionsSubject;
const action = {
type: EntryActionTypes.STOP_TIME_ENTRY_RUNNING_SUCCESS
};
actionSubject.next(action);
expect(component.showNotification).toEqual(true);
});

});
37 changes: 25 additions & 12 deletions src/app/modules/time-clock/pages/time-clock.component.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import { getStatusMessage } from './../../customer-management/store/customer-management.selectors';
import { getActiveTimeEntry } from './../store/entry.selectors';
import { StopTimeEntryRunning } from './../store/entry.actions';
import { Entry } from './../../shared/models/entry.model';
import { Store, select } from '@ngrx/store';
import { Component, OnInit } from '@angular/core';
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
import {getStatusMessage} from './../../time-clock/store/entry.selectors';
import {getActiveTimeEntry} from './../store/entry.selectors';
import {EntryActionTypes, StopTimeEntryRunning} from './../store/entry.actions';
import {Entry} from './../../shared/models/entry.model';
import {Store, select, ActionsSubject} from '@ngrx/store';
import {Component, OnInit} from '@angular/core';
import {AzureAdB2CService} from '../../login/services/azure.ad.b2c.service';
import {Observable, Subscription} from 'rxjs';
import {filter} from 'rxjs/operators';

@Component({
selector: 'app-time-clock',
templateUrl: './time-clock.component.html',
styleUrls: ['./time-clock.component.scss'],
})
export class TimeClockComponent implements OnInit {

username: string;
areFieldsVisible = false;
activeTimeEntry: Entry;
message: string;
message: Observable<string>;
showNotification = false;
isError = false;
actionsSubscription: Subscription;

constructor(private azureAdB2CService: AzureAdB2CService, private store: Store<Entry>) {
constructor(private azureAdB2CService: AzureAdB2CService, private store: Store<Entry>, private actionsSubject$: ActionsSubject) {
}

ngOnInit() {
this.message = this.store.pipe(select(getStatusMessage));
this.username = this.azureAdB2CService.isLogin() ? this.azureAdB2CService.getName() : '';
this.store.pipe(select(getActiveTimeEntry)).subscribe((activeTimeEntry) => {
this.activeTimeEntry = activeTimeEntry;
Expand All @@ -31,8 +36,12 @@ export class TimeClockComponent implements OnInit {
this.areFieldsVisible = false;
}
});
this.store.pipe(select(getStatusMessage)).subscribe((valueMessage) => {
this.message = valueMessage;

this.actionsSubscription = this.actionsSubject$.pipe(
filter((action: any) => (action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS) ||
action.type === EntryActionTypes.STOP_TIME_ENTRY_RUNNING_SUCCESS)
).subscribe((action) => {
this.displayNotification();
});
}

Expand All @@ -41,4 +50,8 @@ export class TimeClockComponent implements OnInit {
this.areFieldsVisible = false;
}

displayNotification() {
this.showNotification = true;
setTimeout(() => ((this.showNotification = false), (this.isError = false)), 3000);
}
}
11 changes: 10 additions & 1 deletion src/app/modules/time-clock/store/entry.reducer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ describe('entryReducer', () => {
expect(state.isLoading).toEqual(true);
});

it('on CreateEntrySuccess, message is updated', () => {
const entryToCreate: NewEntry = { project_id: '1', start_date: '2020-04-21T19:51:36.559000+00:00' };
const action = new actions.CreateEntrySuccess(entryToCreate);
const state = entryReducer(initialState, action);

expect(state.message).toEqual('You clocked-in successfully');
});

it('on CreateEntryFail, entryList equal []', () => {
const action = new actions.CreateEntryFail('error');
const state = entryReducer(initialState, action);
Expand Down Expand Up @@ -81,12 +89,13 @@ describe('entryReducer', () => {
expect(state.isLoading).toEqual(true);
});

it('on StopTimeEntryRunningSuccess, active to be null', () => {
it('on StopTimeEntryRunningSuccess, active is null and message is updated', () => {
const action = new actions.StopTimeEntryRunningSuccess('id');

const state = entryReducer(initialState, action);

expect(state.active).toEqual(null);
expect(state.message).toEqual('You clocked-out successfully');
});

it('on UpdateActiveEntryFail, isLoading is false', () => {
Expand Down
4 changes: 2 additions & 2 deletions src/app/modules/time-clock/store/entry.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const entryReducer = (state: EntryState = initialState, action: EntryActi
active: action.payload,
entryList: [...state.entryList, action.payload],
isLoading: false,
message: 'Entry Created',
message: 'You clocked-in successfully',
};
}

Expand Down Expand Up @@ -102,7 +102,7 @@ export const entryReducer = (state: EntryState = initialState, action: EntryActi
...state,
active: null,
isLoading: false,
message: 'You just clocked-out successfully',
message: 'You clocked-out successfully',
};
}

Expand Down
10 changes: 10 additions & 0 deletions src/app/modules/time-clock/store/entry.selectors.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as selectors from './entry.selectors';

describe('Entry selectors', () => {
it('should select the message', () => {
const anyMessage = 'my-message';
const entryState = { message: anyMessage };

expect(selectors.getStatusMessage.projector(entryState)).toBe(anyMessage);
});
});
2 changes: 2 additions & 0 deletions src/app/modules/time-clock/store/entry.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ const getEntryState = createFeatureSelector('entries');
export const getActiveTimeEntry = createSelector(getEntryState, (state: EntryState) => {
return state.active;
});

export const getStatusMessage = createSelector(getEntryState, (state: EntryState) => state.message);