diff --git a/src/app/app.component.html b/src/app/app.component.html index 0680b43f9..199f49375 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1 +1,13 @@ + + + + + + + + + + + + diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 391675003..ccb2e143c 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -95,6 +95,11 @@ import { SearchUserComponent } from './modules/shared/components/search-user/sea import { TimeRangeCustomComponent } from './modules/reports/components/time-range-custom/time-range-custom.component'; import { TimeRangeHeaderComponent } from './modules/reports/components/time-range-custom/time-range-header/time-range-header.component'; import { TimeRangeOptionsComponent } from './modules/reports/components/time-range-custom/time-range-options/time-range-options.component'; +import { InternetConnectionStatusComponent } from './modules/internet-connection-status/internet-connection-status.component'; +import { FastDirective} from './modules/internet-connection-status/internet-connection-directives/fast.directive'; +import { SlowDirective} from './modules/internet-connection-status/internet-connection-directives/slow.directive'; +import { OfflineDirective} from './modules/internet-connection-status/internet-connection-directives/offline.directive'; +import { ConnectionDirective } from './modules/internet-connection-status/internet-connection-directives/connection.directive'; const maskConfig: Partial = { validation: false, @@ -154,6 +159,11 @@ const maskConfig: Partial = { TimeRangeCustomComponent, TimeRangeHeaderComponent, TimeRangeOptionsComponent, + InternetConnectionStatusComponent, + SlowDirective, + FastDirective, + OfflineDirective, + ConnectionDirective ], imports: [ NgxMaskModule.forRoot(maskConfig), diff --git a/src/app/modules/activities-management/store/activity-management.effects.ts b/src/app/modules/activities-management/store/activity-management.effects.ts index 3b16dd15d..08d08e11c 100644 --- a/src/app/modules/activities-management/store/activity-management.effects.ts +++ b/src/app/modules/activities-management/store/activity-management.effects.ts @@ -9,13 +9,15 @@ import { ToastrService } from 'ngx-toastr'; import * as actions from './activity-management.actions'; import { Activity, ActivityStatus } from './../../shared/models/activity.model'; import { ActivityService } from './../services/activity.service'; +import { StatusNetworkService } from '../../shared/services/status-network.service'; @Injectable() export class ActivityEffects { constructor( private actions$: Actions, private activityService: ActivityService, - private toastrService: ToastrService + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService ) { } @Effect() @@ -27,7 +29,7 @@ export class ActivityEffects { return new actions.LoadActivitiesSuccess(activities); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadActivitiesFail(error)); }) ) @@ -45,7 +47,7 @@ export class ActivityEffects { return new actions.CreateActivitySuccess(activityData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.CreateActivityFail(error)); }) ) @@ -66,7 +68,7 @@ export class ActivityEffects { return new actions.ArchiveActivitySuccess(activity); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.ArchiveActivityFail(error)); }) ) @@ -84,7 +86,7 @@ export class ActivityEffects { return new actions.UpdateActivitySuccess(activityData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateActivityFail(error)); }) ) @@ -106,7 +108,7 @@ export class ActivityEffects { return new actions.UnarchiveActivitySuccess(activityData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UnarchiveActivityFail(error)); }) ) diff --git a/src/app/modules/customer-management/components/projects-type/services/project-type.service.ts b/src/app/modules/customer-management/components/projects-type/services/project-type.service.ts index 6d9e672c2..685c5d0df 100644 --- a/src/app/modules/customer-management/components/projects-type/services/project-type.service.ts +++ b/src/app/modules/customer-management/components/projects-type/services/project-type.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; - import { environment } from '../../../../../../environments/environment'; import { ProjectType } from '../../../../shared/models'; diff --git a/src/app/modules/customer-management/components/projects-type/store/project-type.effects.ts b/src/app/modules/customer-management/components/projects-type/store/project-type.effects.ts index d11294c8a..2646dc762 100644 --- a/src/app/modules/customer-management/components/projects-type/store/project-type.effects.ts +++ b/src/app/modules/customer-management/components/projects-type/store/project-type.effects.ts @@ -9,13 +9,15 @@ import * as actions from './project-type.actions'; import { ProjectType } from '../../../../shared/models'; import { ProjectTypeService } from '../services/project-type.service'; import { ToastrService } from 'ngx-toastr'; +import { StatusNetworkService } from '../../../../shared/services/status-network.service'; @Injectable() export class ProjectTypeEffects { constructor( private actions$: Actions, private projectTypeService: ProjectTypeService, - private toastrService: ToastrService + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService ) { } @Effect() @@ -27,7 +29,7 @@ export class ProjectTypeEffects { return new actions.LoadProjectTypesSuccess(projectTypes); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadProjectTypesFail(error)); }) ) @@ -45,7 +47,7 @@ export class ProjectTypeEffects { return new actions.CreateProjectTypeSuccess(projectTypeData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.CreateProjectTypeFail(error)); }) ) @@ -63,7 +65,7 @@ export class ProjectTypeEffects { return new actions.DeleteProjectTypeSuccess(protectTypeId); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.DeleteProjectTypeFail(error)); }) ) @@ -81,7 +83,7 @@ export class ProjectTypeEffects { return new actions.UpdateProjectTypeSuccess(projectTypeData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateProjectTypeFail(error)); }) ) diff --git a/src/app/modules/customer-management/components/projects/components/store/project.effects.ts b/src/app/modules/customer-management/components/projects/components/store/project.effects.ts index 5067b1b85..a80d12d29 100644 --- a/src/app/modules/customer-management/components/projects/components/store/project.effects.ts +++ b/src/app/modules/customer-management/components/projects/components/store/project.effects.ts @@ -8,13 +8,15 @@ import { ProjectService } from '../services/project.service'; import * as actions from './project.actions'; import { ToastrService } from 'ngx-toastr'; import { Status } from 'src/app/modules/shared/models'; +import { StatusNetworkService } from '../../../../../shared/services/status-network.service'; @Injectable() export class ProjectEffects { constructor( private actions$: Actions, private projectService: ProjectService, - private toastrService: ToastrService + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService ) { } @Effect() @@ -26,7 +28,7 @@ export class ProjectEffects { return new actions.LoadProjectsSuccess(projects); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadProjectsFail(error)); }) ) @@ -42,7 +44,7 @@ export class ProjectEffects { return new actions.LoadCustomerProjectsSuccess(project); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadCustomerProjectsFail(error)); }) ) @@ -58,7 +60,7 @@ export class ProjectEffects { return new actions.LoadRecentProjectsSuccess(projects); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadRecentProjectsFail(error)); }) ) @@ -76,7 +78,7 @@ export class ProjectEffects { return new actions.CreateProjectSuccess(projectData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.CreateProjectFail(error)); }) ) @@ -94,7 +96,7 @@ export class ProjectEffects { return new actions.UpdateProjectSuccess(projectData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateProjectFail(error)); }) ) @@ -112,7 +114,7 @@ export class ProjectEffects { return new actions.DeleteProjectSuccess(projectId); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.DeleteProjectFail(error)); }) ) @@ -133,7 +135,7 @@ export class ProjectEffects { return new actions.UnarchiveProjectSuccess(projectData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UnarchiveProjectFail(error)); }) ) diff --git a/src/app/modules/customer-management/store/customer-management.effects.ts b/src/app/modules/customer-management/store/customer-management.effects.ts index 7485396b7..7c5e7030f 100644 --- a/src/app/modules/customer-management/store/customer-management.effects.ts +++ b/src/app/modules/customer-management/store/customer-management.effects.ts @@ -9,13 +9,15 @@ import { ToastrService } from 'ngx-toastr'; import { CustomerService } from '../services/customer.service'; import * as actions from './customer-management.actions'; import { Status } from '../../shared/models/customer.model'; +import { StatusNetworkService } from '../../shared/services/status-network.service'; @Injectable() export class CustomerEffects { constructor( private actions$: Actions, private customerService: CustomerService, - private toastrService: ToastrService + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService ) { } @Effect() @@ -27,7 +29,7 @@ export class CustomerEffects { return new actions.LoadCustomersSuccess(customers); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadCustomersFail(error)); } ) @@ -46,7 +48,7 @@ export class CustomerEffects { return new actions.CreateCustomerSuccess(customerData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.CreateCustomerFail(error)); }) ) @@ -64,7 +66,7 @@ export class CustomerEffects { return new actions.DeleteCustomerSuccesss(customerId); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.DeleteCustomerFail(error)); }) ) @@ -82,7 +84,7 @@ export class CustomerEffects { return new actions.UpdateCustomerSuccess(customerData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateCustomerFail(error)); }) ) @@ -104,7 +106,7 @@ export class CustomerEffects { return new actions.UpdateCustomerSuccess(customerData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateCustomerFail(error)); }) ) diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.spec.ts b/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.spec.ts new file mode 100644 index 000000000..7f0ff2649 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.spec.ts @@ -0,0 +1,15 @@ +import { TestBed } from '@angular/core/testing'; +import { ConnectionDirective } from './connection.directive'; + +describe('ConnectionDirective', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ConnectionDirective] + }); + }); + + it('should create an instance', () => { + const directive = new ConnectionDirective('slowSrc', 'fastSrc', 'offlineSrc', undefined); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.ts b/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.ts new file mode 100644 index 000000000..e366eb991 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/connection.directive.ts @@ -0,0 +1,29 @@ +import { Directive, Attribute, ElementRef, OnInit } from '@angular/core'; + +@Directive({ + selector: '[appConnection]' +}) + +export class ConnectionDirective implements OnInit { + constructor( + @Attribute('slowSrc') private slowSrc, + @Attribute('fastSrc') private fastSrc, + @Attribute('offlineSrc') private offlineSrc, + private host: ElementRef + ) { + } + + ngOnInit() { + const { effectiveType } = navigator.connection; + let networkStatus; + if (/\fast-5g|3g|4g/.test(effectiveType)) { + networkStatus = this.fastSrc; + } else if (/\slow-2g|2g/.test(effectiveType)) { + networkStatus = this.slowSrc; + } else { + networkStatus = this.offlineSrc; + } + + this.host.nativeElement.setAttribute('src', networkStatus); + } +} diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.spec.ts b/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.spec.ts new file mode 100644 index 000000000..f738749a3 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.spec.ts @@ -0,0 +1,14 @@ +import { TestBed } from '@angular/core/testing'; +import { FastDirective } from './fast.directive'; + +describe('FastDirective', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [FastDirective] + }); + }); + it('should create an instance', () => { + const directive = new FastDirective(undefined); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.ts b/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.ts new file mode 100644 index 000000000..50faa29a3 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/fast.directive.ts @@ -0,0 +1,11 @@ +import { Directive, TemplateRef } from '@angular/core'; + +@Directive({ + selector: '[appFast]' +}) + +export class FastDirective { + + constructor(public tpl: TemplateRef) { } + +} diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.spec.ts b/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.spec.ts new file mode 100644 index 000000000..bf6960c45 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.spec.ts @@ -0,0 +1,14 @@ +import { TestBed } from '@angular/core/testing'; +import { OfflineDirective } from './offline.directive'; + +describe('FastDirective', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [OfflineDirective] + }); + }); + it('should create an instance', () => { + const directive = new OfflineDirective(undefined); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.ts b/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.ts new file mode 100644 index 000000000..986c8542c --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/offline.directive.ts @@ -0,0 +1,11 @@ +import { Directive, TemplateRef } from '@angular/core'; + +@Directive({ + selector: '[appOffline]' +}) + +export class OfflineDirective { + + constructor(public tpl: TemplateRef) { } + +} diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.spec.ts b/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.spec.ts new file mode 100644 index 000000000..59287917a --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.spec.ts @@ -0,0 +1,14 @@ +import { TestBed } from '@angular/core/testing'; +import { SlowDirective } from './slow.directive'; + +describe('FastDirective', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SlowDirective] + }); + }); + it('should create an instance', () => { + const directive = new SlowDirective(undefined); + expect(directive).toBeTruthy(); + }); +}); diff --git a/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.ts b/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.ts new file mode 100644 index 000000000..9095e34a1 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-directives/slow.directive.ts @@ -0,0 +1,11 @@ +import { Directive, TemplateRef } from '@angular/core'; + +@Directive({ + selector: '[appSlow]' +}) + +export class SlowDirective { + + constructor(public tpl: TemplateRef) { } + +} diff --git a/src/app/modules/internet-connection-status/internet-connection-status.component.html b/src/app/modules/internet-connection-status/internet-connection-status.component.html new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/modules/internet-connection-status/internet-connection-status.component.spec.ts b/src/app/modules/internet-connection-status/internet-connection-status.component.spec.ts new file mode 100644 index 000000000..4f273fff8 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-status.component.spec.ts @@ -0,0 +1,88 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { InternetConnectionStatusComponent } from './internet-connection-status.component'; +import { ToastrService } from 'ngx-toastr'; +import { of } from 'rxjs'; + +describe('InternetConnectionStatusComponent', () => { + let component: InternetConnectionStatusComponent; + let fixture: ComponentFixture; + const toastrServiceStub = { + error: () => { + return 'test error'; + }, + warning: () => { + return 'warning error'; + }, + success: () => { + return 'success error'; + } + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ InternetConnectionStatusComponent ], + providers: [{ provide: ToastrService, useValue: toastrServiceStub }] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(InternetConnectionStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('component.isFast should be true when the connection is 4g', () => { + component.connection$ = of('4g'); + fixture.detectChanges(); + component.ngOnInit(); + expect(component.isFast).toBe(true); + }); + + it('component.isFast should be false when the connection is 2g', () => { + component.connection$ = of('2g'); + fixture.detectChanges(); + component.ngOnInit(); + expect(component.isFast).toBe(false); + }); + + it('component.isFast should be false when the connection is Offline', () => { + component.connection$ = of('Offline'); + fixture.detectChanges(); + component.ngOnInit(); + expect(component.isFast).toBe(false); + }); + + it('should change the network type', () => { + component.connection$ = of('5g'); + fixture.detectChanges(); + component.ngOnInit(); + component.connection$.subscribe((networkType: string) => { + expect(networkType).toEqual('5g'); + }); + + jasmine.clock().install(); + jasmine.clock().tick(300); + + component.connection$ = of('2g'); + fixture.detectChanges(); + console.log(window.navigator); + component.connection$.subscribe((networkType: string) => { + expect(networkType).toEqual('2g'); + }); + + jasmine.clock().uninstall(); + }); + + it('should return when window.navigator.connection is undefined', () => { + const attributeCustome = '__defineGetter__'; + window.navigator[attributeCustome]('connection', () => { + return undefined; + }); + fixture = TestBed.createComponent(InternetConnectionStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + const statusInit = component.ngOnInit(); + expect(statusInit).toEqual(undefined); + }); +}); diff --git a/src/app/modules/internet-connection-status/internet-connection-status.component.ts b/src/app/modules/internet-connection-status/internet-connection-status.component.ts new file mode 100644 index 000000000..b1ed09223 --- /dev/null +++ b/src/app/modules/internet-connection-status/internet-connection-status.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit, ContentChild, Attribute} from '@angular/core'; +import { FastDirective} from '../../modules/internet-connection-status/internet-connection-directives/fast.directive'; +import { SlowDirective} from '../../modules/internet-connection-status/internet-connection-directives/slow.directive'; +import { OfflineDirective} from '../../modules/internet-connection-status/internet-connection-directives/offline.directive'; +import { Observable, Subscription } from 'rxjs'; +import { take } from 'rxjs/operators'; +import { ToastrService } from 'ngx-toastr'; + +type Connection = { + effectiveType: string; +}; + +declare global { + interface Navigator { + connection: { + effectiveType: string; + addEventListener: (a: any, b: any) => {}; + removeEventListener: (a: any, b: any) => {}; + }; + } +} + + +@Component({ + selector: 'app-internet-connection-status', + templateUrl: './internet-connection-status.component.html' +}) +export class InternetConnectionStatusComponent implements OnInit { + isFast = true; + connectionType: string; + networkType: string; + @ContentChild(FastDirective) fast: FastDirective; + @ContentChild(SlowDirective) slow: SlowDirective; + @ContentChild(OfflineDirective) offline: OfflineDirective; + + private subscription: Subscription; + + constructor(@Attribute('listen') private withChanges: boolean, private toastrService: ToastrService) { + } + + connection$ = new Observable((observer) => { + const { effectiveType } = navigator.connection; + observer.next(effectiveType); + + const onConnectionChange = () => { + this.networkType = navigator.connection.effectiveType; + observer.next(this.networkType); + }; + + navigator.connection.addEventListener('change', onConnectionChange); + + return () => { + navigator.connection.removeEventListener('change', onConnectionChange); + observer.complete(); + }; + }); + + ngOnInit(){ + const connection = navigator.connection; + console.log('navigator component', connection); + if (!connection || !connection.effectiveType) { + return; + } + + this.subscription = this.connection$ + .pipe(take(this.withChanges ? Number.POSITIVE_INFINITY : 1)) + .subscribe((effectiveType: string) => { + + this.connectionType = effectiveType; + + if (/\fast-5g|3g|4g/.test(effectiveType)) { + this.isFast = true; + return; + } + if (/\slow-2g|2g/.test(effectiveType)) { + this.toastrService.warning('Caution your connection is slow'); + this.isFast = false; + return; + } + this.toastrService.warning('Caution your connection is slow'); + this.isFast = false; + }); + } +} diff --git a/src/app/modules/shared/services/status-network.service.spec.ts b/src/app/modules/shared/services/status-network.service.spec.ts new file mode 100644 index 000000000..8ae41a12b --- /dev/null +++ b/src/app/modules/shared/services/status-network.service.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from '@angular/core/testing'; +import { catchError } from 'rxjs/operators'; +import { IndividualConfig, ToastrService } from 'ngx-toastr'; +import { StatusNetworkService } from './status-network.service'; + +export interface ErrorType { + error: any; + message?: string; + isError?: boolean; +} +describe('StatusNetworkService', () => { + let service: StatusNetworkService; + const toastrServiceStub = { + error: (message?: string, title?: string, override?: Partial) => { }, + warning: (message?: string, title?: string, override?: Partial) => { } + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ToastrService, { + provide: ToastrService, useValue: toastrServiceStub + }] + }); + service = TestBed.inject(StatusNetworkService); + }); + + it('showTypeToastrServiceAlert is error', () => { + const errorType: ErrorType = {error: catchError, message: 'The server is disconnected', isError: true}; + spyOn(toastrServiceStub, 'error'); + service.showTypeToastrServiceAlert(errorType); + expect(toastrServiceStub.error).toHaveBeenCalled(); + }); + + it('showTypeToastrServiceAlert is warning with message', () => { + const errorType: ErrorType = {error: catchError, message: 'The server is disconnected', isError: false}; + spyOn(toastrServiceStub, 'warning'); + service.showTypeToastrServiceAlert(errorType); + expect(toastrServiceStub.warning).toHaveBeenCalled(); + }); + + it('showTypeToastrServiceAlert is warning without message', () => { + const errorType: ErrorType = {error: catchError, isError: false}; + spyOn(toastrServiceStub, 'warning'); + service.showTypeToastrServiceAlert(errorType); + expect(toastrServiceStub.warning).toHaveBeenCalled(); + }); +}); diff --git a/src/app/modules/shared/services/status-network.service.ts b/src/app/modules/shared/services/status-network.service.ts new file mode 100644 index 000000000..e268962cb --- /dev/null +++ b/src/app/modules/shared/services/status-network.service.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { ToastrService } from 'ngx-toastr'; + +export interface ErrorType { + error: any; + message?: string; + isError?: boolean; +} + +@Injectable({ + providedIn: 'root' +}) +export class StatusNetworkService { + + constructor( + private toastrService: ToastrService + ) { } + + showTypeToastrServiceAlert(dataError: ErrorType) { + const { isError = false, message = 'The server is disconnected', error } = dataError; + const effectiveTypenetwork = navigator.connection; + + if (effectiveTypenetwork.effectiveType === '2g') { + this.toastrService.warning('Your request was not completed, your connection is slow'); + return; + } + + if (!isError) { + this.toastrService.warning(message); + return; + } + + const errorMessa = (error.error && error.error.message ? error.error.message : + 'There was an error in the server, your request was not completed'); + this.toastrService.error(errorMessa); + + }} diff --git a/src/app/modules/time-clock/store/entry.effects.ts b/src/app/modules/time-clock/store/entry.effects.ts index f9fc9d769..77c355c3e 100644 --- a/src/app/modules/time-clock/store/entry.effects.ts +++ b/src/app/modules/time-clock/store/entry.effects.ts @@ -8,11 +8,16 @@ import { catchError, map, mergeMap, switchMap } from 'rxjs/operators'; import { EntryService } from '../services/entry.service'; import * as actions from './entry.actions'; import * as moment from 'moment'; +import { StatusNetworkService } from '../../shared/services/status-network.service'; @Injectable() export class EntryEffects { - constructor(private actions$: Actions, private entryService: EntryService, private toastrService: ToastrService) { - } + constructor( + private actions$: Actions, + private entryService: EntryService, + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService + ) { } @Effect() switchEntryRunning$: Observable = this.actions$.pipe( @@ -32,7 +37,8 @@ export class EntryEffects { return new actions.ClockIn(entry); }), catchError((error) => { - this.toastrService.warning('We could not perform this operation, try again later'); + this.statusNetworkService.showTypeToastrServiceAlert( + {error, message: 'We could not perform this operation, try again later', isError: false}); return of(new actions.StopTimeEntryRunningFail(error)); }) ) @@ -51,7 +57,8 @@ export class EntryEffects { return new actions.LoadEntriesSummarySuccess(response); }), catchError((error) => { - this.toastrService.warning('Your summary information could not be loaded'); + this.statusNetworkService.showTypeToastrServiceAlert( + {error, message: 'Your summary information could not be loaded', isError: false}); return of(new actions.LoadEntriesSummaryFail()); }) ) @@ -96,7 +103,7 @@ export class EntryEffects { this.entryService.loadEntries({ month: date.month, year: date.year }).pipe( map((entries) => new actions.LoadEntriesSuccess(entries)), catchError((error) => { - this.toastrService.warning(`The data could not be loaded`); + this.statusNetworkService.showTypeToastrServiceAlert({error, message: 'The data could not be loaded', isError: false}); return of(new actions.LoadEntriesFail(error)); }) ) @@ -118,7 +125,7 @@ export class EntryEffects { return new actions.CreateEntrySuccess(entryData); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.CreateEntryFail(error.error.message)); }) ) @@ -146,7 +153,8 @@ export class EntryEffects { if (error.status === 404) { return of(new actions.CreateEntry(entry)); } else { - this.toastrService.error('We could not clock you in, try again later.'); + this.statusNetworkService.showTypeToastrServiceAlert( + {error, message: 'We could not clock you in, try again later.', isError: true}); return of(new actions.CreateEntryFail('Error')); } }) @@ -165,7 +173,7 @@ export class EntryEffects { return new actions.DeleteEntrySuccess(entryId); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.DeleteEntryFail(error)); }) ) @@ -183,7 +191,7 @@ export class EntryEffects { return new actions.UpdateEntrySuccess(entryResponse); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateEntryFail(error)); }) ) @@ -201,7 +209,7 @@ export class EntryEffects { return new actions.UpdateEntrySuccess(entryResponse); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateEntryFail(error)); }) ) @@ -219,7 +227,7 @@ export class EntryEffects { return new actions.StopTimeEntryRunningSuccess(response); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.StopTimeEntryRunningFail(error.error.message)); }) ) @@ -246,7 +254,7 @@ export class EntryEffects { } }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.UpdateCurrentOrLastEntryFail('error')); }) ) @@ -279,7 +287,7 @@ export class EntryEffects { return new actions.RestartEntrySuccess(entryResponse); }), catchError((error) => { - this.toastrService.error(error.error.message, 'This entry could not be restarted'); + this.statusNetworkService.showTypeToastrServiceAlert({error, message: 'This entry could not be restarted', isError: true}); return of(new actions.RestartEntryFail(error)); }) ) diff --git a/src/app/modules/users/store/user.effects.ts b/src/app/modules/users/store/user.effects.ts index 87f97eaef..6090f07a0 100644 --- a/src/app/modules/users/store/user.effects.ts +++ b/src/app/modules/users/store/user.effects.ts @@ -7,10 +7,16 @@ import { map, catchError, mergeMap } from 'rxjs/operators'; import { ToastrService } from 'ngx-toastr'; import { UsersService } from '../services/users.service'; import * as actions from './user.actions'; +import { StatusNetworkService } from '../../shared/services/status-network.service'; @Injectable() export class UserEffects { - constructor(private actions$: Actions, private userService: UsersService, private toastrService: ToastrService) {} + constructor( + private actions$: Actions, + private userService: UsersService, + private toastrService: ToastrService, + private statusNetworkService: StatusNetworkService + ) {} @Effect() loadUsers$: Observable = this.actions$.pipe( @@ -21,7 +27,7 @@ export class UserEffects { return new actions.LoadUsersSuccess(users); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.LoadUsersFail(error)); }) ) @@ -39,7 +45,7 @@ export class UserEffects { return new actions.AddUserToGroupSuccess(response); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.AddUserToGroupFail(error)); }) ) @@ -57,7 +63,7 @@ export class UserEffects { return new actions.RemoveUserFromGroupSuccess(response); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.RemoveUserFromGroupFail(error)); }) ) @@ -75,7 +81,7 @@ export class UserEffects { return new actions.GrantUserRoleSuccess(response); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.GrantUserRoleFail(error)); }) ) @@ -93,7 +99,7 @@ export class UserEffects { return new actions.RevokeUserRoleSuccess(response); }), catchError((error) => { - this.toastrService.error(error.error.message); + this.statusNetworkService.showTypeToastrServiceAlert({error, isError: true}); return of(new actions.RevokeUserRoleFail(error)); }) )