diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 830a671..eca5125 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,14 +21,16 @@ import {AppComponent} from './app.component'; import {CountComponent} from './count/count.component'; import {FooterComponent} from './footer/footer.component'; import {HomeComponent} from './home/home.component'; +import {LabelLayoutComponent} from './label-layout/label-layout.component'; import {LoadingComponent} from './loading/loading.component'; import {NavbarComponent} from './navbar/navbar.component'; +import {PrintLayoutComponent} from './print-layout/print-layout.component'; import {PrintComponent} from './print/print.component'; import {SampleComponent} from './sample/sample.component'; import {ApiService} from './services/api.service'; +import {CacheService} from './services/cache.service'; +import {SettingsService} from './services/settings.service'; import {SettingsComponent} from './settings/settings.component'; -import { LabelLayoutComponent } from './label-layout/label-layout.component'; -import { PrintLayoutComponent } from './print-layout/print-layout.component'; /** * This function is used internal to get a string instance of the `` value from `index.html`. @@ -48,16 +50,16 @@ export function getBaseHref(platformLocation: PlatformLocation): string { @NgModule({ declarations: [ AppComponent, - LoadingComponent, - FooterComponent, - NavbarComponent, - HomeComponent, - SampleComponent, CountComponent, - SettingsComponent, - PrintComponent, + FooterComponent, + HomeComponent, LabelLayoutComponent, + LoadingComponent, + NavbarComponent, + PrintComponent, PrintLayoutComponent, + SampleComponent, + SettingsComponent, ], imports: [ BrowserAnimationsModule, @@ -65,25 +67,26 @@ export function getBaseHref(platformLocation: PlatformLocation): string { FormlyModule, FormsModule, HttpClientModule, - MatProgressSpinnerModule, - ReactiveFormsModule, - MatCardModule, - MatInputModule, - MatToolbarModule, MatButtonModule, - MatIconModule, + MatCardModule, MatFormFieldModule, - QRCodeSVGModule, - AppRoutingModule, + MatIconModule, + MatInputModule, MatOptionModule, + MatProgressSpinnerModule, MatSelectModule, - // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters) + MatToolbarModule, + QRCodeSVGModule, + ReactiveFormsModule, + AppRoutingModule, // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters) ], providers: [ - {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}}, ApiService, + CacheService, + SettingsService, {provide: 'APP_ENVIRONMENT', useClass: ThisEnvironment}, {provide: APP_BASE_HREF, useFactory: getBaseHref, deps: [PlatformLocation]}, + {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}}, ], bootstrap: [AppComponent], entryComponents: [] diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts index 2f9714b..b1978af 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/home/home.component.spec.ts @@ -1,19 +1,35 @@ +import {APP_BASE_HREF} from '@angular/common'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; import {async, ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing'; +import {ApiService} from '../services/api.service'; +import {CacheService} from '../services/cache.service'; +import {MockEnvironment} from '../testing/environment.mock'; import { HomeComponent } from './home.component'; describe('HomeComponent', () => { let component: HomeComponent; let fixture: ComponentFixture; + let httpMock: HttpTestingController; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [ HomeComponent ] + declarations: [ HomeComponent ], + imports: [ + HttpClientTestingModule, + ], + providers: [ + ApiService, + CacheService, + {provide: 'APP_ENVIRONMENT', useClass: MockEnvironment}, + {provide: APP_BASE_HREF, useValue: '/'}, + ], }) .compileComponents(); })); beforeEach(() => { + httpMock = TestBed.inject(HttpTestingController); fixture = TestBed.createComponent(HomeComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index 73acf06..67459c4 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -1,4 +1,6 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit} from '@angular/core'; +import {ApiService} from '../services/api.service'; +import {CacheService} from '../services/cache.service'; @Component({ selector: 'app-home', @@ -7,9 +9,33 @@ import { Component, OnInit } from '@angular/core'; }) export class HomeComponent implements OnInit { - constructor() { } + constructor( + private cacheService: CacheService, + private apiService: ApiService, + ) { + } ngOnInit(): void { + const cachedRecords = this.cacheService.getRecords(); + let numSuccess = 0; + if (cachedRecords && cachedRecords.length > 0) { + cachedRecords.forEach(r => { + this.apiService.addSample(r).subscribe(() => { + numSuccess++; + console.log('cachedRecords', cachedRecords); + console.log('numSuccess', numSuccess); + + if (numSuccess === cachedRecords.length) { + console.log('Cache cleared.'); + this.cacheService.clearCache(); + } + }, error => { + console.log('Cannot connect to server. Cache not cleared.'); + }); + }); + } else { + console.log('No cached records to upload.'); + } } } diff --git a/src/app/loading/loading.component.html b/src/app/loading/loading.component.html index 6577960..37b2db8 100644 --- a/src/app/loading/loading.component.html +++ b/src/app/loading/loading.component.html @@ -1,5 +1,14 @@ -
- {{message || ''}} - +
+
+ +
+
+ {{message || ''}} +
{{message || '...'}} diff --git a/src/app/navbar/navbar.component.html b/src/app/navbar/navbar.component.html index b01a01f..2059cfb 100644 --- a/src/app/navbar/navbar.component.html +++ b/src/app/navbar/navbar.component.html @@ -1,7 +1,7 @@ - + diff --git a/src/app/navbar/navbar.component.ts b/src/app/navbar/navbar.component.ts index f3b7ccb..4cc5d45 100644 --- a/src/app/navbar/navbar.component.ts +++ b/src/app/navbar/navbar.component.ts @@ -9,14 +9,15 @@ import {TestingLocation} from '../models/testingLocation.interface'; }) export class NavbarComponent implements OnInit { @Input() testingLocation: TestingLocation; - locationId: string; constructor(private settingsService: SettingsService) { - const settings = this.settingsService.getSettings(); - this.locationId = settings.locationId; } ngOnInit(): void { } + get locationId(): string { + const settings = this.settingsService.getSettings(); + return settings.locationId; + } } diff --git a/src/app/print/print.component.html b/src/app/print/print.component.html index 2767fc1..d47bbe9 100644 --- a/src/app/print/print.component.html +++ b/src/app/print/print.component.html @@ -10,7 +10,11 @@

Print Labels

- +
+ + + + diff --git a/src/app/print/print.component.scss b/src/app/print/print.component.scss index 9165b26..c5f9a71 100644 --- a/src/app/print/print.component.scss +++ b/src/app/print/print.component.scss @@ -1,8 +1,21 @@ @import 'src/config'; +::ng-deep .loading { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: white; + z-index: 100; +} + @media screen { #media-print { display: none !important; } - #media-screen { display: flex !important; } + #media-screen { + display: flex !important; + + } } @media print { @@ -34,4 +47,3 @@ background-color: $brand-gray-tint-2; height: 500px; } - diff --git a/src/app/print/print.component.ts b/src/app/print/print.component.ts index d423807..1f604b6 100644 --- a/src/app/print/print.component.ts +++ b/src/app/print/print.component.ts @@ -6,6 +6,7 @@ import {AppDefaults} from '../models/appDefaults.interface'; import {LabelLayout} from '../models/labelLayout.interface'; import {Sample} from '../models/sample.interface'; import {ApiService} from '../services/api.service'; +import {CacheService} from '../services/cache.service'; import {SettingsService} from '../services/settings.service'; @Component({ @@ -20,13 +21,14 @@ export class PrintComponent implements AfterViewInit { settings: AppDefaults; @ViewChild('saveAndPrintButton') saveAndPrintButton: MatButton; @ViewChild('doneButton') doneButton: MatButton; - isSaved = false; + isSaved: boolean; constructor( private api: ApiService, private route: ActivatedRoute, private changeDetector: ChangeDetectorRef, - private settingsService: SettingsService + private settingsService: SettingsService, + private cacheService: CacheService, ) { this.dateCreated = new Date(); this.route.queryParamMap.subscribe(queryParamMap => { @@ -34,6 +36,11 @@ export class PrintComponent implements AfterViewInit { this.initials = queryParamMap.get('initials'); }); this.settings = this.settingsService.getSettings(); + this.isSaved = false; + + this.save(s => { + this.isSaved = true; + }); } ngAfterViewInit() { @@ -63,7 +70,7 @@ export class PrintComponent implements AfterViewInit { headEl.appendChild(styleEl); } - saveAndPrint() { + save(callback: (s: Sample) => void) { const id = createQrCodeValue( this.barCode, this.initials, @@ -78,8 +85,22 @@ export class PrintComponent implements AfterViewInit { location: this.settings.locationId, }; - this.api.addSample(newSample).subscribe(() => { - this.isSaved = true; + this.api.addSample(newSample).subscribe((result) => { + console.log('addSample subscribe callback'); + callback(result); + }, err => { + if (err) { + console.error(err); + } + + const cachedRecords = this.cacheService.saveRecord(newSample); + console.log('cachedRecords', cachedRecords); + callback(newSample); + }); + } + + saveAndPrint() { + this.save(s => { window.print(); this.doneButton.focus(); this.changeDetector.detectChanges(); diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index 8ec0bc8..705c1e2 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -34,11 +34,11 @@ export class ApiService { } /** Add new sample */ - addSample(sample: Sample): Observable { + addSample(sample: Sample): Observable { const url = this.apiRoot + this.endpoints.sample; return this.httpClient - .post(url, sample) + .post(url, sample) .pipe(catchError(err => this._handleError(err))); } diff --git a/src/app/services/cache.service.spec.ts b/src/app/services/cache.service.spec.ts new file mode 100644 index 0000000..345e523 --- /dev/null +++ b/src/app/services/cache.service.spec.ts @@ -0,0 +1,20 @@ +import {TestBed} from '@angular/core/testing'; +import {CacheService} from './cache.service'; + +describe('CacheService', () => { + let service: CacheService; + + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + CacheService, + ], + })); + + beforeEach(() => { + service = TestBed.inject(CacheService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/cache.service.ts b/src/app/services/cache.service.ts new file mode 100644 index 0000000..8b77a82 --- /dev/null +++ b/src/app/services/cache.service.ts @@ -0,0 +1,54 @@ +import {Injectable} from '@angular/core'; +import serializeJs from 'serialize-javascript'; +import {Sample} from '../models/sample.interface'; + +@Injectable({ + providedIn: 'root' +}) +export class CacheService { + // Default form field and data values + records: Sample[] = []; + + // localStorage key + private localStorageKey = 'cachedRecords'; + + // Deserializes settings from local storage and returns AppDefaults instance + getStoredRecords(): Sample[] { + // tslint:disable-next-line:no-eval + return (eval(`(${localStorage.getItem(this.localStorageKey)})`) || []) as Sample[]; + } + + // Returns true if settings are found in local storage + hasStoredRecords(): boolean { + return this.getStoredRecords().length > 0; + } + + // Returns records from local storage, or [] if none have been saved yet. + getRecords(): Sample[] { + if (this.hasStoredRecords()) { + return this.getStoredRecords(); + } else { + return this.saveRecords(this.records); + } + } + + // Serializes given record and adds it to cache in local storage + saveRecord(newRecord: Sample): Sample[] { + const records = this.getRecords(); + records.push(newRecord); + return this.saveRecords(records); + } + + // Serializes multiple given records and stores them in local storage + saveRecords(newRecords: Sample[]): Sample[] { + localStorage.setItem(this.localStorageKey, serializeJs(newRecords)); + return newRecords; + } + + // Clears cached records. + clearCache(): Sample[] { + return this.saveRecords([]); + } +} + + diff --git a/src/app/services/settings.service.spec.ts b/src/app/services/settings.service.spec.ts new file mode 100644 index 0000000..52ba15a --- /dev/null +++ b/src/app/services/settings.service.spec.ts @@ -0,0 +1,20 @@ +import {TestBed} from '@angular/core/testing'; +import {SettingsService} from './settings.service'; + +describe('SettingsService', () => { + let service: SettingsService; + + beforeEach(() => TestBed.configureTestingModule({ + providers: [ + SettingsService, + ], + })); + + beforeEach(() => { + service = TestBed.inject(SettingsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +});