Skip to content

Commit c2e8d33

Browse files
authored
Merge pull request #217 from ioet/216-add-support-for-notifications
fix: #216 add suport for toast messages closes: #202 closes: #201 closes: #218
2 parents c18a02f + 9c80bf4 commit c2e8d33

33 files changed

+238
-233
lines changed

angular.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
],
2626
"styles": [
2727
"./node_modules/bootstrap/dist/css/bootstrap.min.css",
28-
"src/styles.scss"
28+
"src/styles.scss",
29+
"node_modules/ngx-toastr/toastr.css"
2930
],
3031
"scripts": [
3132
"./node_modules/jquery/dist/jquery.min.js",

package-lock.json

Lines changed: 8 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
},
1313
"private": true,
1414
"dependencies": {
15-
"@angular/animations": "~9.0.3",
15+
"@angular/animations": "^9.0.7",
1616
"@angular/common": "~9.0.3",
1717
"@angular/compiler": "~9.0.3",
1818
"@angular/core": "~9.0.3",
@@ -29,6 +29,7 @@
2929
"minimist": "^1.2.5",
3030
"msal": "^1.2.1",
3131
"ngx-pagination": "^5.0.0",
32+
"ngx-toastr": "^12.0.1",
3233
"rxjs": "~6.5.4",
3334
"tslib": "^1.10.0",
3435
"zone.js": "~0.10.2"

src/app/app.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ import { Component } from '@angular/core';
77
})
88
export class AppComponent {
99
title = 'time-tracker';
10+
1011
}

src/app/app.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
2+
import { ToastrModule } from 'ngx-toastr';
13
import { CommonModule } from '@angular/common';
24
import { BrowserModule } from '@angular/platform-browser';
35
import { NgModule } from '@angular/core';
@@ -54,7 +56,6 @@ import { CreateProjectTypeComponent } from './modules/customer-management/compon
5456
import { CustomerEffects } from './modules/customer-management/store/customer-management.effects';
5557
import { EntryEffects } from './modules/time-clock/store/entry.effects';
5658
import { InjectTokenInterceptor } from './modules/shared/interceptors/inject.token.interceptor';
57-
import { NotificationComponent } from './modules/shared/components/notification/notification/notification.component';
5859

5960
@NgModule({
6061
declarations: [
@@ -89,11 +90,11 @@ import { NotificationComponent } from './modules/shared/components/notification/
8990
ProjectTypeListComponent,
9091
CreateProjectTypeComponent,
9192
EntryFieldsComponent,
92-
NotificationComponent,
9393
],
9494
imports: [
9595
CommonModule,
9696
BrowserModule,
97+
BrowserAnimationsModule,
9798
AppRoutingModule,
9899
FormsModule,
99100
ReactiveFormsModule,
@@ -116,6 +117,7 @@ import { NotificationComponent } from './modules/shared/components/notification/
116117
ProjectTypeEffects,
117118
EntryEffects,
118119
]),
120+
ToastrModule.forRoot()
119121
],
120122
providers: [
121123
{

src/app/modules/activities-management/pages/activities-management.component.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
<div class="container">
2-
<app-notification [notificationMsg]="notificationMsg" [isError]="isError" *ngIf="showNotification"></app-notification>
32
<div class="row">
43
<div class="col">
54
<app-create-activity></app-create-activity>

src/app/modules/activities-management/pages/activities-management.component.spec.ts

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,37 @@ import { ActivityService } from './../services/activity.service';
66
import { ActivitiesManagementComponent } from './activities-management.component';
77
import { ActionsSubject } from '@ngrx/store';
88
import { ActivityManagementActionTypes } from '../store';
9+
import { ToastrService, ToastrModule, IndividualConfig } from 'ngx-toastr';
910

1011
describe('ActivitiesManagementComponent', () => {
1112
let component: ActivitiesManagementComponent;
1213
let fixture: ComponentFixture<ActivitiesManagementComponent>;
1314
let activityService: ActivityService;
1415
const activitiesFromApi: Activity[] = [{ id: '123', name: 'aaa', description: 'xxx' }];
16+
const toastrService = {
17+
success: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { },
18+
error: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { }
19+
};
20+
let injectedToastrService;
1521

1622
beforeEach(async(() => {
1723
const actionSub: ActionsSubject = new ActionsSubject();
1824
TestBed.configureTestingModule({
25+
imports: [ToastrModule.forRoot()],
1926
declarations: [ActivitiesManagementComponent],
20-
providers: [ActivityService, HttpClient, HttpHandler, { provide: ActionsSubject, useValue: actionSub }],
27+
providers: [ActivityService, HttpClient, HttpHandler,
28+
{ provide: ActionsSubject, useValue: actionSub },
29+
{ provide: ToastrService, useValue: toastrService }
30+
],
2131
}).compileComponents();
2232
}));
2333

2434
beforeEach(() => {
2535
fixture = TestBed.createComponent(ActivitiesManagementComponent);
2636
component = fixture.componentInstance;
2737
fixture.detectChanges();
28-
38+
injectedToastrService = TestBed.inject(ToastrService);
39+
console.log(injectedToastrService);
2940
activityService = TestBed.inject(ActivityService);
3041
spyOn(activityService, 'getActivities').and.returnValue(of(activitiesFromApi));
3142
});
@@ -47,9 +58,17 @@ describe('ActivitiesManagementComponent', () => {
4758
});
4859

4960
it('has a succesfull message on CREATE_ACTIVITY_SUCCESS', () => {
61+
spyOn(injectedToastrService, 'success');
5062
component.setDataNotification(ActivityManagementActionTypes.CREATE_ACTIVITY_SUCCESS);
5163

52-
expect(component.notificationMsg).toBe('The activity has been saved successfully.');
64+
expect(injectedToastrService.success).toHaveBeenCalled();
65+
});
66+
67+
it('has a succesfull message on UPDATE_ACTIVITY_SUCCESS', () => {
68+
spyOn(injectedToastrService, 'success');
69+
component.setDataNotification(ActivityManagementActionTypes.UPDATE_ACTIVITY_SUCCESS);
70+
71+
expect(injectedToastrService.success).toHaveBeenCalled();
5372
});
5473

5574
it('should destroy the subscription', () => {
@@ -62,38 +81,49 @@ describe('ActivitiesManagementComponent', () => {
6281
});
6382

6483
it('#setDataNotification should show an success notification with DELETE_ACTIVITY_SUCCESS case', () => {
84+
spyOn(injectedToastrService, 'success').and.returnValue(of(activitiesFromApi));
6585
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
6686
const action = {
6787
type: '[ActivityManagement] DELETE_ACTIVITY_SUCESS',
6888
};
6989
actionSubject.next(action);
7090

7191
component.setDataNotification(action.type);
72-
expect(component.showNotification).toBeTrue();
73-
expect(component.isError).toBeFalse();
92+
expect(injectedToastrService.success).toHaveBeenCalled();
7493
});
7594

76-
it('#setDataNotification should show an error notification with CREATE_ACTIVITY_FAIL case', () => {
77-
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
78-
const action = {
79-
type: '[ActivityManagement] CREATE_ACTIVITY_FAIL',
80-
};
81-
actionSubject.next(action);
95+
it('shows an error notification with CREATE_ACTIVITY_FAIL case', () => {
96+
spyOn(injectedToastrService, 'error');
8297

83-
component.setDataNotification(action.type);
84-
expect(component.showNotification).toBeTrue();
85-
expect(component.isError).toBeTrue();
98+
component.setDataNotification(ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL);
99+
100+
expect(injectedToastrService.error).toHaveBeenCalled();
101+
});
102+
103+
it('shows an error notification with UPDATE_ACTIVITY_FAIL case', () => {
104+
spyOn(injectedToastrService, 'error');
105+
106+
component.setDataNotification(ActivityManagementActionTypes.UPDATE_ACTIVITY_FAIL);
107+
108+
expect(injectedToastrService.error).toHaveBeenCalled();
109+
});
110+
111+
it('shows an error notification with DELETE_ACTIVITY_FAIL case', () => {
112+
spyOn(injectedToastrService, 'error');
113+
114+
component.setDataNotification(ActivityManagementActionTypes.DELETE_ACTIVITY_FAIL);
115+
116+
expect(injectedToastrService.error).toHaveBeenCalled();
86117
});
87118

88119
it('#setDataNotification should not show an error notification with incorrect action', () => {
120+
spyOn(injectedToastrService, 'success').and.returnValue(of(activitiesFromApi));
89121
const actionSubject = TestBed.inject(ActionsSubject) as ActionsSubject;
90122
const action = {
91123
type: '[ActivityManagement] TEST',
92124
};
93125
actionSubject.next(action);
94126

95127
component.setDataNotification(action.type);
96-
expect(component.showNotification).toBeFalse();
97-
expect(component.isError).toBeFalse();
98128
});
99129
});

src/app/modules/activities-management/pages/activities-management.component.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,17 @@ import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { ActionsSubject } from '@ngrx/store';
33
import { Subscription } from 'rxjs';
44
import { ActivityManagementActionTypes } from '../store';
5+
import { ToastrService } from 'ngx-toastr';
56

67
@Component({
78
selector: 'app-activities-management',
89
templateUrl: './activities-management.component.html',
910
styleUrls: ['./activities-management.component.scss'],
1011
})
1112
export class ActivitiesManagementComponent implements OnInit, OnDestroy {
12-
notificationMsg = '';
13-
showNotification = false;
14-
isError = false;
1513
actionsSubscription: Subscription;
1614

17-
constructor(private actionsSubject$: ActionsSubject) {}
15+
constructor(private actionsSubject$: ActionsSubject, private toastrService: ToastrService) {}
1816

1917
ngOnInit() {
2018
this.actionsSubscription = this.actionsSubject$.subscribe((action) => {
@@ -27,26 +25,31 @@ export class ActivitiesManagementComponent implements OnInit, OnDestroy {
2725
}
2826

2927
setDataNotification(action: any) {
30-
this.showNotification = true;
3128
switch (action) {
3229
case ActivityManagementActionTypes.CREATE_ACTIVITY_SUCCESS: {
33-
this.notificationMsg = 'The activity has been saved successfully.';
30+
this.toastrService.success('The activity has been saved successfully.');
31+
break;
32+
}
33+
case ActivityManagementActionTypes.UPDATE_ACTIVITY_SUCCESS: {
34+
this.toastrService.success('The activity has been saved successfully.');
3435
break;
3536
}
3637
case ActivityManagementActionTypes.DELETE_ACTIVITY_SUCCESS: {
37-
this.notificationMsg = 'The activity has been removed successfully.';
38+
this.toastrService.success('The activity has been removed successfully.');
39+
break;
40+
}
41+
case ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL: {
42+
this.toastrService.error('An unexpected error happened, please try again later.');
3843
break;
3944
}
40-
case ActivityManagementActionTypes.CREATE_ACTIVITY_FAIL || ActivityManagementActionTypes.DELETE_ACTIVITY_FAIL: {
41-
this.notificationMsg = 'An unexpected error happened, please try again later.';
42-
this.isError = true;
45+
case ActivityManagementActionTypes.UPDATE_ACTIVITY_FAIL: {
46+
this.toastrService.error('An unexpected error happened, please try again later.');
4347
break;
4448
}
45-
default: {
46-
this.showNotification = false;
49+
case ActivityManagementActionTypes.DELETE_ACTIVITY_FAIL : {
50+
this.toastrService.error('An unexpected error happened, please try again later.');
4751
break;
4852
}
4953
}
50-
setTimeout(() => ((this.showNotification = false), (this.isError = false)), 3000);
5154
}
5255
}

src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.html

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,5 @@
11
<div class="container">
22
<form style="width: 600px;" [formGroup]="customerForm" (ngSubmit)="onSubmit(customerForm.value)">
3-
<div class="form-group">
4-
<div
5-
*ngIf="showAlert && messageToShow !== ''"
6-
[ngClass]="{'bg-secondary': messageToShow == 'Customer created successfully!' || messageToShow == 'Customer updated successfully!',
7-
'bg-primary': messageToShow !== 'Customer created successfully!' }"
8-
class="alert alert-dismissible fade fade-in show text-white"
9-
role="alert"
10-
>
11-
<strong>{{ messageToShow }}</strong>
12-
</div>
13-
</div>
143
<div class="form-group">
154
<input
165
class="form-control form-control-sm"

src/app/modules/customer-management/components/customer-info/components/create-customer/create-customer.spec.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@ import { CreateCustomerComponent } from './create-customer';
66
import { CustomerState, CreateCustomer } from 'src/app/modules/customer-management/store';
77
import { ResetCustomerToEdit, UpdateCustomer } from './../../../../store/customer-management.actions';
88
import { Customer } from 'src/app/modules/shared/models';
9+
import { ToastrService, IndividualConfig } from 'ngx-toastr';
910

1011
describe('CreateCustomerComponent', () => {
1112
let component: CreateCustomerComponent;
1213
let fixture: ComponentFixture<CreateCustomerComponent>;
1314
let store: MockStore<CustomerState>;
15+
const toastrService = {
16+
success: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { },
17+
error: (message?: string, title?: string, override?: Partial<IndividualConfig>) => { }
18+
};
1419

1520
const state = {
1621
data: [],
@@ -28,17 +33,19 @@ describe('CreateCustomerComponent', () => {
2833
beforeEach(async(() => {
2934
TestBed.configureTestingModule({
3035
declarations: [CreateCustomerComponent],
31-
providers: [FormBuilder, provideMockStore({ initialState: state })],
36+
providers: [
37+
FormBuilder,
38+
provideMockStore({ initialState: state }),
39+
{ provide: ToastrService, useValue: toastrService },
40+
],
3241
}).compileComponents();
33-
}));
3442

35-
beforeEach(() => {
3643
fixture = TestBed.createComponent(CreateCustomerComponent);
3744
component = fixture.componentInstance;
3845
fixture.detectChanges();
3946
store = TestBed.inject(MockStore);
4047
store.setState(state);
41-
});
48+
}));
4249

4350
afterEach(() => {
4451
fixture.destroy();
@@ -87,30 +94,20 @@ describe('CreateCustomerComponent', () => {
8794

8895
it('should be enable tabs and show message Customer created successfully! ', () => {
8996
component.areTabsActive = false;
90-
component.messageToShow = '';
91-
92-
spyOn(store, 'dispatch');
9397

9498
component.ngOnInit();
9599
component.onSubmit(customerData);
96-
component.setStatusOnScreen('Customer created successfully!');
97100

98-
expect(component.messageToShow).toEqual('Customer created successfully!');
99101
expect(component.areTabsActive).toBeTrue();
100102
});
101103

102104
it('should be disabled tabs and show message An error occurred, try again later. ', () => {
103105
component.areTabsActive = false;
104-
component.messageToShow = '';
105-
106-
spyOn(store, 'dispatch');
107106

108107
component.ngOnInit();
109108
component.onSubmit(customerData);
110-
component.setStatusOnScreen('An error occurred, try again later.');
111109

112-
expect(component.messageToShow).toEqual('An error occurred, try again later.');
113-
expect(component.areTabsActive).toBeFalse();
110+
expect(component.areTabsActive).toBeTruthy();
114111
});
115112

116113
it('set data to update ', () => {
@@ -119,6 +116,6 @@ describe('CreateCustomerComponent', () => {
119116
component.ngOnInit();
120117
component.setDataToUpdate(customerData);
121118

122-
expect(component.messageToShow).toEqual(undefined);
119+
expect(store.dispatch).toHaveBeenCalledTimes(2);
123120
});
124121
});

0 commit comments

Comments
 (0)