diff --git a/src/app/configs/configs-routing.module.ts b/src/app/configs/configs-routing.module.ts index 94a2ea09..2fcdf80e 100644 --- a/src/app/configs/configs-routing.module.ts +++ b/src/app/configs/configs-routing.module.ts @@ -2,7 +2,6 @@ import { NgModule } from "@angular/core"; import { RouterModule, Routes } from "@angular/router"; import { AuthGuardService } from "../shared"; import { ConfigsComponent } from "./configs.component"; -import { TransportProviderComponent } from "./transport-provider/transport-provider.component"; const routes: Routes = [ { @@ -12,7 +11,10 @@ const routes: Routes = [ children: [ { path: "transport-providers", - component: TransportProviderComponent, + loadChildren: () => + import(`./transport-providers/transport-providers.module`).then( + (m) => m.TransportProvidersModule + ), canActivate: [AuthGuardService], }, ], diff --git a/src/app/configs/configs.component.html b/src/app/configs/configs.component.html index b36d11b9..0680b43f 100644 --- a/src/app/configs/configs.component.html +++ b/src/app/configs/configs.component.html @@ -1,4 +1 @@ -

- configs works! -

diff --git a/src/app/configs/configs.module.ts b/src/app/configs/configs.module.ts index b287d7ee..9831d82d 100644 --- a/src/app/configs/configs.module.ts +++ b/src/app/configs/configs.module.ts @@ -2,12 +2,11 @@ import { NgModule } from "@angular/core"; import { CommonModule } from "@angular/common"; import { ConfigsComponent } from "./configs.component"; import { ConfigsRoutingModule } from "./configs-routing.module"; -import { TransportProviderComponent } from "./transport-provider/transport-provider.component"; -import { RecordsTableModule } from "../shared/components/records-table/records-table.module"; +import { TransportProvidersModule } from "./transport-providers/transport-providers.module"; @NgModule({ - imports: [CommonModule, ConfigsRoutingModule, RecordsTableModule], - declarations: [ConfigsComponent, TransportProviderComponent], + imports: [CommonModule, ConfigsRoutingModule, TransportProvidersModule], + declarations: [ConfigsComponent], }) export class ConfigsModule { constructor() { diff --git a/src/app/configs/transport-provider/transport-provider.component.ts b/src/app/configs/transport-provider/transport-provider.component.ts deleted file mode 100644 index c498fdb8..00000000 --- a/src/app/configs/transport-provider/transport-provider.component.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Component, OnInit } from "@angular/core"; -import { Observable } from "rxjs"; -import { filter, first } from "rxjs/operators"; -import { TransportProvider } from "src/app/online-orders/shared"; -import { TransportProvidersStoreService } from "src/app/shared/services/transport-providers-store.service"; - -@Component({ - selector: "app-transport-provider", - templateUrl: "./transport-provider.component.html", - styleUrls: ["./transport-provider.component.css"], -}) -export class TransportProviderComponent implements OnInit { - public transportProviders$: Observable; - public excludeCols: string[] = ["availabilityRules"]; - - constructor(private store: TransportProvidersStoreService) {} - - handleRowSelect(selectedRecord: TransportProvider): void { - // navigate to single record - console.log(selectedRecord); - } - - ngOnInit(): void { - this.transportProviders$ = this.store.transportProviders$.pipe( - filter((x) => x.length !== 0 || !x), - first() - ); - } -} diff --git a/src/app/configs/transport-provider/transport-provider.component.css b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.css similarity index 100% rename from src/app/configs/transport-provider/transport-provider.component.css rename to src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.css diff --git a/src/app/configs/transport-provider/transport-provider.component.html b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html similarity index 100% rename from src/app/configs/transport-provider/transport-provider.component.html rename to src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.html diff --git a/src/app/configs/transport-provider/transport-provider.component.spec.ts b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.spec.ts similarity index 54% rename from src/app/configs/transport-provider/transport-provider.component.spec.ts rename to src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.spec.ts index 53e07a2f..c54e2054 100644 --- a/src/app/configs/transport-provider/transport-provider.component.spec.ts +++ b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.spec.ts @@ -2,31 +2,31 @@ import { HttpClientModule } from "@angular/common/http"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; -import { TransportProvidersStoreService } from "src/app/shared/services/transport-providers-store.service"; -import { TransportProvidersStoreServiceSpy } from "src/app/shared/testing/services.spy"; +import { TransportProvidersServiceSpy } from "src/app/shared/testing/services.spy"; -import { TransportProviderComponent } from "./transport-provider.component"; +import { TransportProviderListComponent } from "./transport-provider-list.component"; +import { TransportProvidersService } from "src/app/online-orders/transport-providers.service"; describe("c", () => { - let component: TransportProviderComponent; - let fixture: ComponentFixture; + let component: TransportProviderListComponent; + let fixture: ComponentFixture; // const httpClientSpy: any = jasmine.createSpyObj("HttpClient", ["get"]); beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [TransportProviderComponent], + declarations: [TransportProviderListComponent], imports: [HttpClientModule, RouterTestingModule], providers: [ { - provide: TransportProvidersStoreService, - useClass: TransportProvidersStoreServiceSpy, + provide: TransportProvidersService, + useClass: TransportProvidersServiceSpy, }, ], }).compileComponents(); }); beforeEach(() => { - fixture = TestBed.createComponent(TransportProviderComponent); + fixture = TestBed.createComponent(TransportProviderListComponent); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts new file mode 100644 index 00000000..0ce3cf63 --- /dev/null +++ b/src/app/configs/transport-providers/tranport-providers-list/transport-provider-list.component.ts @@ -0,0 +1,29 @@ +import { Component, OnInit } from "@angular/core"; +import { Observable } from "rxjs"; +import { filter, first } from "rxjs/operators"; +import { TransportProvider } from "src/app/online-orders/shared"; +import { TransportProvidersService } from "src/app/online-orders/transport-providers.service"; + +@Component({ + selector: "app-transport-list-provider", + templateUrl: "./transport-provider-list.component.html", + styleUrls: ["./transport-provider-list.component.css"], +}) +export class TransportProviderListComponent implements OnInit { + public transportProviders$: Observable; + public excludeCols: string[] = ["availabilityRules"]; + + constructor(private service: TransportProvidersService) {} + + handleRowSelect(selectedRecord: TransportProvider): void { + // navigate to single record + console.log(selectedRecord); + } + + ngOnInit(): void { + this.transportProviders$ = this.service.getTransportProviders(); + + // filter((x) => x.length !== 0 || !x) + // first() + } +} diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.css new file mode 100644 index 00000000..e69de29b diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html new file mode 100644 index 00000000..90965192 --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.html @@ -0,0 +1 @@ +

transport-providers-edit works!

diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts new file mode 100644 index 00000000..a85fffc5 --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; + +import { TransportProvidersEditComponent } from "./transport-providers-edit.component"; + +describe("TransportProvidersEditComponent", () => { + let component: TransportProvidersEditComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TransportProvidersEditComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TransportProvidersEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts new file mode 100644 index 00000000..29266287 --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers-edit/transport-providers-edit.component.ts @@ -0,0 +1,8 @@ +import { Component, OnInit } from "@angular/core"; + +@Component({ + selector: "app-transport-providers-edit", + templateUrl: "./transport-providers-edit.component.html", + styleUrls: ["./transport-providers-edit.component.css"], +}) +export class TransportProvidersEditComponent {} diff --git a/src/app/configs/transport-providers/transport-providers-routing.module.ts b/src/app/configs/transport-providers/transport-providers-routing.module.ts new file mode 100644 index 00000000..f62c7df6 --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from "@angular/core"; +import { RouterModule, Routes } from "@angular/router"; +import { AuthGuardService } from "src/app/shared/services/auth-guard.service"; +import { TransportProviderListComponent } from "./tranport-providers-list/transport-provider-list.component"; +import { TransportProvidersEditComponent } from "./transport-providers-edit/transport-providers-edit.component"; + +const routes: Routes = [ + { + path: "", + redirectTo: "list", + }, + { + path: "list", + component: TransportProviderListComponent, + canActivate: [AuthGuardService], + }, + { + path: "edit:id", + component: TransportProvidersEditComponent, + canActivate: [AuthGuardService], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class TransportProvidersRoutingModule {} diff --git a/src/app/configs/transport-providers/transport-providers.module.ts b/src/app/configs/transport-providers/transport-providers.module.ts new file mode 100644 index 00000000..4b2e45b7 --- /dev/null +++ b/src/app/configs/transport-providers/transport-providers.module.ts @@ -0,0 +1,15 @@ +import { NgModule } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { TransportProviderListComponent } from "./tranport-providers-list/transport-provider-list.component"; +import { RecordsTableModule } from "src/app/shared/components/records-table/records-table.module"; +import { TransportProvidersRoutingModule } from "./transport-providers-routing.module"; +import { TransportProvidersEditComponent } from "./transport-providers-edit/transport-providers-edit.component"; + +@NgModule({ + declarations: [ + TransportProviderListComponent, + TransportProvidersEditComponent, + ], + imports: [CommonModule, RecordsTableModule, TransportProvidersRoutingModule], +}) +export class TransportProvidersModule {} diff --git a/src/app/menu/load-menu-rules.ts b/src/app/menu/load-menu-rules.ts index 87f53fe5..6310ab42 100644 --- a/src/app/menu/load-menu-rules.ts +++ b/src/app/menu/load-menu-rules.ts @@ -84,6 +84,15 @@ export function loadMenuRules(authList: string[]): Array { icon: "airport_shuttle", routerLink: ["configuration/transport-providers"], authorizedRoles: [LRole.ADMIN], + items: [ + new MenuRule({ + id: 14, + label: "List", + icon: "list", + routerLink: ["configuration/transport-providers/list"], + authorizedRoles: [LRole.ADMIN], + }), + ], }), ], }), diff --git a/src/app/online-orders/transport-providers.service.spec.ts b/src/app/online-orders/transport-providers.service.spec.ts index d655eb86..9038db82 100644 --- a/src/app/online-orders/transport-providers.service.spec.ts +++ b/src/app/online-orders/transport-providers.service.spec.ts @@ -1,27 +1,129 @@ -import { TestBed, inject } from "@angular/core/testing"; +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { TestBed } from "@angular/core/testing"; import { TransportProvidersService } from "./transport-providers.service"; -import { HttpClientModule } from "@angular/common/http"; +import { HttpClient, HttpClientModule } from "@angular/common/http"; import { HttpClientTestingModule, HttpTestingController, } from "@angular/common/http/testing"; +import { MessagesServiceSpy } from "../shared/testing/services.spy"; +import { MessagesService } from "../shared/components/messages/messages.service"; +import { TransportProvider } from "./shared"; +import { of } from "rxjs"; describe("TransportProviderService", () => { + let service: TransportProvidersService; + let clientSpy: jasmine.SpyObj; + let messageSpy: jasmine.SpyObj; + let httpClientSpy: jasmine.SpyObj; + + beforeEach(() => { + httpClientSpy = jasmine.createSpyObj("HttpClient", ["get"]); + + TestBed.configureTestingModule({ + providers: [ + TransportProvidersService, + { + provide: HttpClient, + useValue: httpClientSpy, + }, + { + provide: MessagesService, + useClass: MessagesServiceSpy, + }, + ], + imports: [], + }); + service = TestBed.inject(TransportProvidersService); + httpClientSpy = TestBed.inject(HttpClient) as jasmine.SpyObj; + messageSpy = TestBed.inject( + MessagesService + ) as jasmine.SpyObj; + }); + + it("should be created", () => { + expect(service).toBeTruthy(); + }); + + describe("chaching", () => { + beforeEach(() => { + spyOn(HttpClient.prototype, "get").calls.reset(); + httpClientSpy.get.and.returnValue( + of({ + data: [new TransportProvider()], + }) + ); + }); + + it("when api called, session var is set", (done: DoneFn) => { + service.getTransportProviders().subscribe(() => { + expect( + ( + JSON.parse( + sessionStorage.getItem("tranportProviders") + ) as TransportProvider[] + ).length + ).toBe(1); + done(); + }); + }); + + it("when none, should callApi", (done: DoneFn) => { + service.getTransportProviders().subscribe(() => { + expect(httpClientSpy.get.calls.count()).toBe(1); + done(); + }); + }); + + it("when NOT stale, should not callApi", (done: DoneFn) => { + service.providersAge = Date.now() - 300 * 999; // age logic is in service + + service.getTransportProviders().subscribe(() => { + expect(httpClientSpy.get.calls.count()).toBe(0); + done(); + }); + }); + }); +}); + +describe("TransportProviderServiceHttp", () => { + let service: TransportProvidersService; + let httpMock: HttpTestingController; + let messageSpy: jasmine.SpyObj; + beforeEach(() => { TestBed.configureTestingModule({ - providers: [TransportProvidersService], + providers: [ + { + provide: MessagesService, + useClass: MessagesServiceSpy, + }, + ], imports: [HttpClientModule, HttpClientTestingModule], - }) - .compileComponents() - .catch((e) => console.error(e)); - TestBed.inject(HttpTestingController); + }); + service = TestBed.inject(TransportProvidersService); + httpMock = TestBed.inject(HttpTestingController); + messageSpy = TestBed.inject( + MessagesService + ) as jasmine.SpyObj; }); - it("should be created", inject( - [TransportProvidersService], - (service: TransportProvidersService) => { - expect(service).toBeTruthy(); - } - )); + describe("when http error", () => { + it("should call showErrors toast", (done: DoneFn) => { + service.getTransportProviders().subscribe({ + next: () => done.fail("should have failed with the 500 error"), + error: () => { + expect(messageSpy.showErrors.calls.count()).toBe(1); + done(); + }, + }); + + const testReq = httpMock.expectOne( + "http://localhost:9876/api/transportproviders" + ); + expect(testReq.request.method).toEqual("GET"); + testReq.flush("", { status: 500, statusText: "SErver Error" }); + }); + }); }); diff --git a/src/app/online-orders/transport-providers.service.ts b/src/app/online-orders/transport-providers.service.ts index 9b22df71..cb320bc1 100644 --- a/src/app/online-orders/transport-providers.service.ts +++ b/src/app/online-orders/transport-providers.service.ts @@ -1,12 +1,12 @@ -import { Observable } from "rxjs"; +import { Observable, throwError } from "rxjs"; -import { map } from "rxjs/operators"; -import { Injectable } from "@angular/core"; -import { environment } from "../../environments/environment"; -import { HttpClient } from "@angular/common/http"; +import { catchError, map, pluck, shareReplay, tap } from "rxjs/operators"; +import { Injectable, Optional, SkipSelf } from "@angular/core"; import { TransportProvider } from "./shared/"; import { of } from "rxjs"; - +import { MessagesService } from "../shared/components/messages/messages.service"; +import { HttpClient } from "@angular/common/http"; +import { environment } from "src/environments/environment"; @Injectable({ providedIn: "root", }) @@ -14,7 +14,17 @@ export class TransportProvidersService { uriBase = environment.dataUrl + "/api/transportproviders"; providers = new Array(); providersAge = 0; - constructor(private http: HttpClient) { + constructor( + private http: HttpClient, + private appMessages: MessagesService, + @Optional() @SkipSelf() parentModule?: TransportProvidersService + ) { + // enforce app singleton pattern + if (parentModule) { + throw new Error( + `Machete dev error:${TransportProvidersService.name} is already loaded. Additional imports not needed` + ); + } console.log(".ctor"); } @@ -32,14 +42,26 @@ export class TransportProvidersService { getTransportProviders(): Observable { if (this.isNotStale()) { console.log("returning cache", this.providersAge); - return of(this.providers); + const cache = sessionStorage.getItem("tranportProviders"); + return of(JSON.parse(cache) as TransportProvider[]); } return this.http.get(this.uriBase, { withCredentials: true }).pipe( - map((res) => { - this.providers = res["data"] as TransportProvider[]; + catchError((error) => { + // only expecting one type of error + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const errorAsText: string = error["statusText"] as string; + this.appMessages.showErrors({ + Error: `${errorAsText}: Contact Machete support.`, + }); + console.log(error); + return throwError(error); + }), + pluck("data"), + map((data) => data as TransportProvider[]), + tap((data: TransportProvider[]) => { + sessionStorage.setItem("tranportProviders", JSON.stringify(data)); this.providersAge = Date.now(); - return res["data"] as TransportProvider[]; }) ); } diff --git a/src/app/shared/services/transport-providers-store.service.spec.ts b/src/app/shared/services/transport-providers-store.service.spec.ts deleted file mode 100644 index 77f5d42d..00000000 --- a/src/app/shared/services/transport-providers-store.service.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { HttpClientModule } from "@angular/common/http"; -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { TestBed } from "@angular/core/testing"; -import { Router } from "@angular/router"; -import { RouterSpy } from "../testing"; - -import { TransportProvidersStoreService } from "./transport-providers-store.service"; - -describe("TransportProvidersStoreService", () => { - let service: TransportProvidersStoreService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule, HttpClientTestingModule], - providers: [{ provide: Router, useClass: RouterSpy }], - }); - service = TestBed.inject(TransportProvidersStoreService); - }); - - it("should be created", () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/shared/testing/services.spy.ts b/src/app/shared/testing/services.spy.ts index d41246d6..ec02474b 100644 --- a/src/app/shared/testing/services.spy.ts +++ b/src/app/shared/testing/services.spy.ts @@ -252,6 +252,24 @@ export class TransportRulesServiceSpy { ); } +export class TransportProvidersClientSpy { + apiTransportProvidersGet = jasmine + .createSpy("apiTransportProvidersGet") + .and.returnValue( + observableOf({ + data: [ + new TransportProvider({ + id: 32, + text: "a text label", + availabilityRules: new Array( + new TransportProviderAvailability({ day: 0, available: false }) + ), + }), + ], + }) + ); +} + export class TransportProvidersServiceSpy { getTransportProviders = jasmine .createSpy("getTransportProviders") @@ -303,7 +321,9 @@ export class ReportsServiceSpy { // getReportData } -export class MessagesServiceSpy {} +export class MessagesServiceSpy { + showErrors = jasmine.createSpy("showErrors").and.callThrough(); +} export class ReportsStoreServiceSpy { reports$ = observableOf(