Skip to content

Commit 7a1bfa5

Browse files
author
Juan Gabriel Guzman
committed
feat: #201 Adding clock-in and clock-out notifications
1 parent 0dc8687 commit 7a1bfa5

File tree

7 files changed

+75
-21
lines changed

7 files changed

+75
-21
lines changed

src/app/modules/time-clock/pages/time-clock.component.html

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1+
<app-notification [notificationMsg]="message | async" [isError]="isError" *ngIf="showNotification"></app-notification>
12
<div class="text-center mt-3">
2-
<div class="alert alert-danger" role="alert">
3-
{{message}}
4-
</div>
5-
63
<div class="card">
74
<div class="card-body">
85
<h6 class="text-left"><strong>Summary</strong></h6>

src/app/modules/time-clock/pages/time-clock.component.spec.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StopTimeEntryRunning } from './../store/entry.actions';
1+
import {EntryActionTypes, StopTimeEntryRunning } from './../store/entry.actions';
22
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
33
import { HttpClientTestingModule } from '@angular/common/http/testing';
44
import { provideMockStore, MockStore } from '@ngrx/store/testing';
@@ -9,13 +9,15 @@ import { ProjectListHoverComponent } from '../components';
99
import { ProjectService } from '../../customer-management/components/projects/components/services/project.service';
1010
import { FilterProjectPipe } from '../../shared/pipes';
1111
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
12+
import {ActionsSubject} from '@ngrx/store';
1213

1314
describe('TimeClockComponent', () => {
1415
let component: TimeClockComponent;
1516
let fixture: ComponentFixture<TimeClockComponent>;
1617
let store: MockStore<ProjectState>;
1718
let projectService: ProjectService;
1819
let azureAdB2CService: AzureAdB2CService;
20+
const actionSub: ActionsSubject = new ActionsSubject();
1921
const state = {
2022
projects: {
2123
projects: [{ id: 'id', name: 'name', project_type_id: '' }],
@@ -44,7 +46,10 @@ describe('TimeClockComponent', () => {
4446
TestBed.configureTestingModule({
4547
imports: [HttpClientTestingModule],
4648
declarations: [TimeClockComponent, ProjectListHoverComponent, FilterProjectPipe],
47-
providers: [ProjectService, AzureAdB2CService, provideMockStore({ initialState: state })],
49+
providers: [ProjectService,
50+
AzureAdB2CService,
51+
{ provide: ActionsSubject, useValue: actionSub },
52+
provideMockStore({ initialState: state })],
4853
}).compileComponents();
4954
store = TestBed.inject(MockStore);
5055
}));
@@ -92,4 +97,22 @@ describe('TimeClockComponent', () => {
9297
expect(store.dispatch).toHaveBeenCalledWith(new StopTimeEntryRunning('id'));
9398
});
9499

100+
it('on success create entry, the notification is shown', () => {
101+
const actionSubject = TestBed.get(ActionsSubject) as ActionsSubject;
102+
const action = {
103+
type: EntryActionTypes.CREATE_ENTRY_SUCCESS
104+
};
105+
actionSubject.next(action);
106+
expect(component.showNotification).toEqual(true);
107+
});
108+
109+
it('on success stop entry, the notification is shown', () => {
110+
const actionSubject = TestBed.get(ActionsSubject) as ActionsSubject;
111+
const action = {
112+
type: EntryActionTypes.STOP_TIME_ENTRY_RUNNING_SUCCESS
113+
};
114+
actionSubject.next(action);
115+
expect(component.showNotification).toEqual(true);
116+
});
117+
95118
});
Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1-
import { getStatusMessage } from './../../customer-management/store/customer-management.selectors';
2-
import { getActiveTimeEntry } from './../store/entry.selectors';
3-
import { StopTimeEntryRunning } from './../store/entry.actions';
4-
import { Entry } from './../../shared/models/entry.model';
5-
import { Store, select } from '@ngrx/store';
6-
import { Component, OnInit } from '@angular/core';
7-
import { AzureAdB2CService } from '../../login/services/azure.ad.b2c.service';
1+
import {getStatusMessage} from './../../time-clock/store/entry.selectors';
2+
import {getActiveTimeEntry} from './../store/entry.selectors';
3+
import {EntryActionTypes, StopTimeEntryRunning} from './../store/entry.actions';
4+
import {Entry} from './../../shared/models/entry.model';
5+
import {Store, select, ActionsSubject} from '@ngrx/store';
6+
import {Component, OnInit} from '@angular/core';
7+
import {AzureAdB2CService} from '../../login/services/azure.ad.b2c.service';
8+
import {Observable, Subscription} from 'rxjs';
9+
import {filter} from 'rxjs/operators';
810

911
@Component({
1012
selector: 'app-time-clock',
1113
templateUrl: './time-clock.component.html',
1214
styleUrls: ['./time-clock.component.scss'],
1315
})
1416
export class TimeClockComponent implements OnInit {
15-
1617
username: string;
1718
areFieldsVisible = false;
1819
activeTimeEntry: Entry;
19-
message: string;
20+
message: Observable<string>;
21+
showNotification = false;
22+
isError = false;
23+
actionsSubscription: Subscription;
2024

21-
constructor(private azureAdB2CService: AzureAdB2CService, private store: Store<Entry>) {
25+
constructor(private azureAdB2CService: AzureAdB2CService, private store: Store<Entry>, private actionsSubject$: ActionsSubject) {
2226
}
2327

2428
ngOnInit() {
29+
this.message = this.store.pipe(select(getStatusMessage));
2530
this.username = this.azureAdB2CService.isLogin() ? this.azureAdB2CService.getName() : '';
2631
this.store.pipe(select(getActiveTimeEntry)).subscribe((activeTimeEntry) => {
2732
this.activeTimeEntry = activeTimeEntry;
@@ -31,8 +36,12 @@ export class TimeClockComponent implements OnInit {
3136
this.areFieldsVisible = false;
3237
}
3338
});
34-
this.store.pipe(select(getStatusMessage)).subscribe((valueMessage) => {
35-
this.message = valueMessage;
39+
40+
this.actionsSubscription = this.actionsSubject$.pipe(
41+
filter((action: any) => (action.type === EntryActionTypes.CREATE_ENTRY_SUCCESS) ||
42+
action.type === EntryActionTypes.STOP_TIME_ENTRY_RUNNING_SUCCESS)
43+
).subscribe((action) => {
44+
this.displayNotification();
3645
});
3746
}
3847

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

53+
displayNotification() {
54+
this.showNotification = true;
55+
setTimeout(() => ((this.showNotification = false), (this.isError = false)), 3000);
56+
}
4457
}

src/app/modules/time-clock/store/entry.reducer.spec.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ describe('entryReducer', () => {
3737
expect(state.isLoading).toEqual(true);
3838
});
3939

40+
it('on CreateEntrySuccess, message is updated', () => {
41+
const entryToCreate: NewEntry = { project_id: '1', start_date: '2020-04-21T19:51:36.559000+00:00' };
42+
const action = new actions.CreateEntrySuccess(entryToCreate);
43+
const state = entryReducer(initialState, action);
44+
45+
expect(state.message).toEqual('You clocked-in successfully');
46+
});
47+
4048
it('on CreateEntryFail, entryList equal []', () => {
4149
const action = new actions.CreateEntryFail('error');
4250
const state = entryReducer(initialState, action);
@@ -81,12 +89,13 @@ describe('entryReducer', () => {
8189
expect(state.isLoading).toEqual(true);
8290
});
8391

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

8795
const state = entryReducer(initialState, action);
8896

8997
expect(state.active).toEqual(null);
98+
expect(state.message).toEqual('You clocked-out successfully');
9099
});
91100

92101
it('on UpdateActiveEntryFail, isLoading is false', () => {

src/app/modules/time-clock/store/entry.reducer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export const entryReducer = (state: EntryState = initialState, action: EntryActi
5252
active: action.payload,
5353
entryList: [...state.entryList, action.payload],
5454
isLoading: false,
55-
message: 'Entry Created',
55+
message: 'You clocked-in successfully',
5656
};
5757
}
5858

@@ -102,7 +102,7 @@ export const entryReducer = (state: EntryState = initialState, action: EntryActi
102102
...state,
103103
active: null,
104104
isLoading: false,
105-
message: 'You just clocked-out successfully',
105+
message: 'You clocked-out successfully',
106106
};
107107
}
108108

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import * as selectors from './entry.selectors';
2+
3+
describe('Entry selectors', () => {
4+
it('should select the message', () => {
5+
const anyMessage = 'my-message';
6+
const entryState = { message: anyMessage };
7+
8+
expect(selectors.getStatusMessage.projector(entryState)).toBe(anyMessage);
9+
});
10+
});

src/app/modules/time-clock/store/entry.selectors.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,5 @@ const getEntryState = createFeatureSelector('entries');
77
export const getActiveTimeEntry = createSelector(getEntryState, (state: EntryState) => {
88
return state.active;
99
});
10+
11+
export const getStatusMessage = createSelector(getEntryState, (state: EntryState) => state.message);

0 commit comments

Comments
 (0)