Reduces timeout to 1second. Tweaks 1D barcode design. Prints multiple labels.

This commit is contained in:
Aaron Louie 2020-11-23 10:46:27 -05:00
parent d5dbc6d310
commit fa58a9c7d6
26 changed files with 396 additions and 72 deletions

View File

@ -13,7 +13,8 @@ exports.config = {
'./src/**/*.e2e-spec.ts'
],
capabilities: {
browserName: 'chrome'
browserName: 'chrome',
binary: '/usr/bin/google-chrome-stable'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',

View File

@ -20,11 +20,65 @@ describe('COVID19 Testing Kiosk App', () => {
page.clickAndExpectRoute('#nav_settings', '/settings');
});
it('should navigate to home screen', () => {
page.clickAndExpectRoute('#nav_home', '/');
});
it('should navigate back to sample input screen', () => {
page.clickAndExpectRoute('#nav_home', '/');
});
it('should return to settings screen', async () => {
page.clickAndExpectRoute('#nav_settings', '/settings');
});
it('should change location', async () => {
page.inputText('.location-input input', '9999', true);
});
it('should change label layout', async () => {
const dropdownSelector = 'mat-select-trigger.selected-label-layout';
const optionSelector = '.mat-select-panel .mat-option:nth-child(3) .label-layout-name';
page.waitForVisible(dropdownSelector);
const selectedTextBefore = await page.getElement(dropdownSelector).getText();
page.clickElement(dropdownSelector);
page.waitForVisible(optionSelector);
const optionText = await page.getElement(optionSelector).getText();
expect(selectedTextBefore).not.toEqual(optionText);
page.clickElement(optionSelector);
const selectedText = await page.getElement(dropdownSelector).getText();
expect(selectedText).toEqual(optionText);
});
it('should save settings changes', async () => {
const locSelector = '.location-input input';
const dropdownSelector = 'mat-select-trigger.selected-label-layout';
const locIdBefore = await page.getElement(locSelector).getAttribute('value');
const layoutBefore = await page.getElement(dropdownSelector).getAttribute('value');
page.clickAndExpectRoute('#btn_save', '/');
const navLocation = await page.getElement('#nav_location').getText();
expect(navLocation).toContain(locIdBefore);
page.clickAndExpectRoute('#nav_settings', '/settings');
const locIdAfter = await page.getElement(locSelector).getAttribute('value');
expect(locIdBefore).toEqual(locIdAfter);
const layoutAfter = await page.getElement(dropdownSelector).getAttribute('value');
expect(layoutBefore).toEqual(layoutAfter);
page.clickAndExpectRoute('#btn_save', '/');
});
it('should enter a sample', async () => {
const studentId = '987654321';
const computingId = 'ABC123';
const idFieldSelector = '.cardnum-input input';
const initialsFieldSelector = '.initials-input input';
page.inputText(idFieldSelector, studentId);
page.inputText(initialsFieldSelector, computingId);
page.clickAndExpectRoute('#btn_next', /print/);
page.waitForVisible('#btn_print');
const labelText = page.getElement('app-print-layout').getText();
expect(labelText).toContain(studentId);
expect(labelText).toContain(computingId);
});
});

View File

@ -1,6 +1,8 @@
import {browser, by, element, ElementArrayFinder, ElementFinder, ExpectedConditions} from 'protractor';
import {browser, by, element, ElementArrayFinder, ElementFinder, ExpectedConditions, protractor} from 'protractor';
export class AppPage {
browser = browser;
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
@ -81,9 +83,22 @@ export class AppPage {
});
}
async switchFocusToPrintDialog() {
const browserWindowHandles = await browser.getAllWindowHandles();
console.log('browserWindowHandles', browserWindowHandles);
const driver = browser.driver;
const windowHandles = await driver.getAllWindowHandles();
console.log('windowHandles', windowHandles);
return driver.switchTo().window(windowHandles[0]);
}
waitFor(t: number) {
return browser.sleep(t);
}
waitForAngularEnabled(enabled: boolean) {
return browser.waitForAngularEnabled(enabled);
}
waitForClickable(selector: string) {
const e = this.getElement(selector);
@ -95,9 +110,45 @@ export class AppPage {
return browser.wait(ExpectedConditions.invisibilityOf(e), 5000);
}
waitForVisible(selector: string) {
const e = this.getElement(selector);
return browser.wait(ExpectedConditions.visibilityOf(e), 5000);
// If given CSS selector is found on the page, waits 5 seconds for the element to become visible. If it's not found
// on the page, recursively calls itself maxLoops number of times, waiting 1 second between each call, until the
// element becomes present.
async waitForVisible(selector: string, maxLoops = 5) {
const numElements = await this.getElements(selector).count();
if (numElements > 0) {
const e = await this.getElement(selector);
return browser.wait(
ExpectedConditions.visibilityOf(e),
5000,
`Element "${selector}" is still not visible after waiting for 5 seconds.`
);
} else if (maxLoops > 0) {
await this.waitFor(1000);
await this.waitForVisible(selector, maxLoops - 1);
} else {
expect(numElements).toBeGreaterThan(0, `Element "${selector}" is not present on the page.`);
}
}
inputText(selector: string, textToEnter: string, clearFirst?: boolean) {
expect(this.getElements(selector).count()).toEqual(1);
const field = this.getElement(selector);
this.waitForAngularEnabled(true);
this.waitForVisible(selector, 100);
if (clearFirst) {
field.clear();
expect(field.getAttribute('value')).toEqual('');
}
field.sendKeys(textToEnter);
expect(field.getAttribute('value')).toEqual(textToEnter);
}
async pressTabKey100Times() {
for (let i = 0; i < 100; i++) {
await browser.actions().sendKeys(protractor.Key.TAB).perform();
}
}
}

View File

@ -1,7 +1,7 @@
import {formatDate} from '@angular/common';
export const createQrCodeValue = (
barCode: string,
cardNum: string,
initials: string,
dateCreated: Date,
locationId: string,
@ -9,13 +9,15 @@ export const createQrCodeValue = (
barcodeType: string
): string => {
const is1D = (barcodeType === 'code128');
const dateFormat = is1D ? 'yyMMdd' : 'yyyyMMddHHmm';
const locId = is1D ? locationId.slice(3, 4) : locationId;
const compId = is1D ? '' : initials.toUpperCase();
const longDate = formatDate(dateCreated, 'yyyyMMddHHmm', 'en-us');
const dateString = is1D ? longDate.slice(3, 10) : longDate;
const locId = is1D ? '' : locationId;
const valArray = [
barCode,
initials.toUpperCase(),
formatDate(dateCreated, dateFormat, 'en-us'),
cardNum,
compId,
dateString,
locId,
];
return valArray.join(delimiter);

View File

@ -5,6 +5,7 @@ import {CountComponent} from './count/count.component';
import {PrintComponent} from './print/print.component';
import {SampleComponent} from './sample/sample.component';
import {SettingsComponent} from './settings/settings.component';
import {MultipleLabelsComponent} from './multiple-labels/multiple-labels.component';
export const routes: Routes = [
{
@ -32,6 +33,11 @@ export const routes: Routes = [
pathMatch: 'full',
component: SettingsComponent
},
{
path: 'multiple',
pathMatch: 'full',
component: MultipleLabelsComponent
}
];
@NgModule({

View File

@ -36,6 +36,7 @@ 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 { MultipleLabelsComponent } from './multiple-labels/multiple-labels.component';
/**
* This function is used internal to get a string instance of the `<base href="" />` value from `index.html`.
@ -62,6 +63,7 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
FooterComponent,
LabelLayoutComponent,
LoadingComponent,
MultipleLabelsComponent,
NavbarComponent,
PrintComponent,
PrintLayoutComponent,

View File

@ -43,7 +43,7 @@ export const labelLayouts = {
export const defaultOptions: AppDefaultsOptions = {
barCodeNumLength: 9, // Number of digits in Bar Code.
barCodeRegExp: /^[\d]{14}$|^[\d]{9}$/, // Pattern for Bar Code data.
cardNumRegExp: /^[\d]{14}$|^[\d]{9}$/, // Pattern for Bar Code data.
// Scanned barcodes will be either 9 or 14 digits long.
// Manually-entered ID numbers will be exactly 9 digits long.
countsCollection: 'counts', // Name of collection for Line Counts in Firebase.

View File

@ -9,12 +9,11 @@
>
<defs>
<g id="up_arrow">
<g transform="translate(2.5, 2)">
<g transform="translate(-2.5, -2) scale(0.1)">
<path d="M 0,0 H 24 V 24 H 0 Z" style="fill:none"/>
<path d="M 20,11 H 7.83 L 13.42,5.41 12,4 4,12 12,20 13.41,18.59 7.83,13 H 20 Z" />
</g>
<g transform="translate(0, 2)">
<text x="1" y="0">UP</text>
<g transform="translate(2, -2)">
<path d="M12 4l-1.41 1.41L16.17 11H4v2h12.17l-5.58 5.59L12 20l8-8z" transform="scale(0.1)"/>
</g>
</g>
</g>
</defs>
@ -30,16 +29,26 @@
y="0"
rx="2"
/>
<use xlink:href="#up_arrow" x="2" y="2" />
<g transform="translate(3, 8.2)">
<use xlink:href="#up_arrow" x="71" y="1" />
<use xlink:href="#up_arrow" x="71" y="22" />
<!-- <rect x="18" y="9" width="40" height="10" fill="#FF0000" />-->
<g transform="translate(13, 9)">
<g
appBarcodeSvg
[format]="'code128'"
[height]="40"
[height]="10000"
[value]="sample.barcode"
[width]="288"
transform="scale(0.08625)"/>
[width]="50000"
transform="scale(.00035)"/>
</g>
</g>
<text x="50%" y="23" style="font-size: 3px">{{sample.barcode}}</text>
<text x="50%" y="6" style="font-size: 2.7px">
#{{sample.student_id}}
{{sample.initials}}
</text>
<text x="50%" y="9" style="font-size: 2.7px">
{{sample.date | date:'yyyy-MM-dd HH:mm'}}
{{sample.location}}
</text>
<text x="50%" y="23" style="font-size: 2.7px">{{sample.barcode}}</text>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -21,7 +21,7 @@ describe('LabelLayoutComponent', () => {
fixture = TestBed.createComponent(LabelLayoutComponent);
component = fixture.componentInstance;
component.dateCreated = new Date();
component.barCode = '123456789';
component.cardNum = '123456789';
component.initials = 'abc';
fixture.detectChanges();
});

View File

@ -12,7 +12,7 @@ import {SettingsService} from '../services/settings.service';
})
export class LabelLayoutComponent implements OnInit {
@Input() dateCreated: Date;
@Input() barCode: string;
@Input() cardNum: string;
@Input() initials: string;
settings: AppDefaults;
sample: Sample;
@ -25,9 +25,9 @@ export class LabelLayoutComponent implements OnInit {
ngOnInit() {
this.sample = {
barcode: '',
student_id: this.barCode,
student_id: this.cardNum,
initials: this.initials,
date: new Date(),
date: this.dateCreated,
location: this.settings.locationId,
};

View File

@ -3,7 +3,7 @@ import {LabelLayout} from './labelLayout.interface';
export interface AppDefaultsOptions {
barCodeNumLength?: number;
barCodeRegExp?: RegExp | string;
cardNumRegExp?: RegExp | string;
countsCollection?: string;
dateDisplayFormat?: string;
dateEncodedFormat?: string;
@ -20,7 +20,7 @@ export interface AppDefaultsOptions {
export class AppDefaults {
barCodeNumLength: number;
barCodeRegExp: RegExp;
cardNumRegExp: RegExp;
countsCollection: string;
dateDisplayFormat: string;
dateEncodedFormat: string;

View File

@ -35,7 +35,7 @@ export class LabelLayout {
get dimensions() {
return {
pageWidth: this._toUnits(this.pageWidth),
pageHeight: this._toUnits(this.pageHeight),
pageHeight: this._toUnits(this.pageHeight * this.numCopies),
};
}

View File

@ -0,0 +1,17 @@
<div id="media-print" *ngIf="samples && samples.length > 0">
<div
class="print-layout"
[style]="{width: pageWidth, height: pageHeight}"
[gdRows]="pagesToGridFractions"
gdAlignColumns="center"
gdAlignRows="center"
>
<ng-container *ngFor="let sample of samples; let i = index">
<app-label-layout
[cardNum]="sample.student_id"
[initials]="sample.initials.toUpperCase()"
[dateCreated]="sample.date"
></app-label-layout>
</ng-container>
</div>
</div>

View File

@ -0,0 +1,48 @@
@media screen {
#media-print { display: none !important; }
#media-screen {
display: flex !important;
}
}
@media print {
@page {
margin: 0 !important;
padding: 0 !important;
}
::ng-deep html,
::ng-deep body {
margin: 0 !important;
padding: 0 !important;
background-color: transparent;
overflow: hidden;
}
::ng-deep .no-print,
::ng-deep mat-card,
::ng-deep app-navbar,
::ng-deep app-footer {
display: none;
}
#media-print {
display: block !important;
margin: 0 !important;
padding: 0 !important;
background-color: blue;
.print-layout {
display: grid !important;
background-color: transparent;
}
.page-break {
page-break-before: always;
}
}
#media-screen { display: none !important; }
}

View File

@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MultipleLabelsComponent } from './multiple-labels.component';
describe('MultipleLabelsComponent', () => {
let component: MultipleLabelsComponent;
let fixture: ComponentFixture<MultipleLabelsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ MultipleLabelsComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(MultipleLabelsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,100 @@
import {AfterViewChecked, AfterViewInit, Component, OnInit} from '@angular/core';
import {Sample} from '../models/sample.interface';
import {LabelLayout} from '../models/labelLayout.interface';
import {SettingsService} from '../services/settings.service';
import {AppDefaults} from '../models/appDefaults.interface';
import {createQrCodeValue} from '../_util/qrCode';
@Component({
selector: 'app-multiple-labels',
templateUrl: './multiple-labels.component.html',
styleUrls: ['./multiple-labels.component.scss']
})
export class MultipleLabelsComponent implements OnInit, AfterViewInit {
samples: Sample[];
layout: LabelLayout;
settings: AppDefaults;
numLabels = 10;
constructor(private settingsService: SettingsService) {
const startingId = 391;
this.settings = this.settingsService.getSettings();
this.layout = new LabelLayout(this.settings.labelLayout);
this.layout.numCopies = this.numLabels;
this.samples = Array(this.numLabels).fill('').map((x, i) => {
const idBase = (startingId + i).toString(10).padStart(3, '0');
const studentId = idBase + idBase + idBase;
const compId = Array(3)
.fill('')
.map((y, j) => this.randomChar(j))
.join('') + idBase;
const sample: Sample = {
barcode: '',
student_id: studentId,
initials: compId,
location: this.settings.locationId,
date: new Date(),
};
sample.barcode = createQrCodeValue(
sample.student_id,
sample.initials,
sample.date,
sample.location,
this.layout.delimiter,
this.layout.barcodeType
);
return sample;
});
console.log('samples', this.samples);
}
get pagesToGridFractions(): string {
return this.samples.map(() => '1fr').join(' ');
}
get pageHeight(): string {
return `${this.layout.pageHeight * this.numLabels}${this.layout.units}`;
}
get pageWidth(): string {
return this.layout.dimensions.pageWidth;
}
ngOnInit(): void {
}
ngAfterViewInit() {
// Inject the print CSS, with dynamic layout settings, into the DOM.
const printCss = `
@media print {
@page {
size: ${this.layout.dimensions.pageWidth} ${this.layout.pageHeight}${this.layout.units};
}
html,
body {
width: ${this.layout.dimensions.pageWidth} !important;
height: ${this.layout.dimensions.pageHeight} !important;
}
}
`;
const headEl = document.getElementsByTagName('head')[0];
const styleEl = document.createElement('style');
styleEl.setAttribute('type', 'text/css');
styleEl.appendChild(document.createTextNode(printCss));
headEl.appendChild(styleEl);
}
private randomChar(i) {
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numbers = '1234567890';
// If it's the first character, return letters only.
const from = (i === 0) ? letters : letters.concat(numbers);
return from.charAt(Math.floor(Math.random() * from.length));
}
}

View File

@ -13,7 +13,7 @@
>
<app-label-layout
*ngFor="let col of columns"
[barCode]="barCode"
[cardNum]="cardNum"
[initials]="initials.toUpperCase()"
[dateCreated]="dateCreated"
></app-label-layout>

View File

@ -10,7 +10,7 @@ import {SettingsService} from '../services/settings.service';
})
export class PrintLayoutComponent implements AfterViewInit {
@Input() dateCreated: Date;
@Input() barCode: string;
@Input() cardNum: string;
@Input() initials: string;
settings: AppDefaults;
layout: LabelLayout;

View File

@ -16,13 +16,14 @@
*ngIf="isSaved; else loadingMessage"
>
<app-print-layout
[barCode]="barCode"
[cardNum]="cardNum"
[initials]="initials.toUpperCase()"
[dateCreated]="dateCreated"
></app-print-layout>
</mat-card-content>
<mat-card-actions fxLayoutAlign="center center">
<button
id="btn_print"
#saveAndPrintButton="matButton"
mat-flat-button
class="btn-lg"
@ -42,9 +43,11 @@
</mat-card>
</div>
<div id="media-print">
<div
id="media-print"
>
<app-print-layout
[barCode]="barCode"
[cardNum]="cardNum"
[initials]="initials.toUpperCase()"
[dateCreated]="dateCreated"
></app-print-layout>

View File

@ -33,7 +33,7 @@ describe('PrintComponent', () => {
{provide: APP_BASE_HREF, useValue: '/'},
{
provide: ActivatedRoute,
useValue: {queryParamMap: of(convertToParamMap({barCode: '123456789', initials: 'abc'}))}
useValue: {queryParamMap: of(convertToParamMap({cardNum: '123456789', initials: 'abc'}))}
},
]
})

View File

@ -15,7 +15,7 @@ import {SettingsService} from '../services/settings.service';
styleUrls: ['./print.component.scss']
})
export class PrintComponent implements AfterViewInit {
barCode: string;
cardNum: string;
initials: string;
dateCreated: Date;
settings: AppDefaults;
@ -33,7 +33,7 @@ export class PrintComponent implements AfterViewInit {
) {
this.dateCreated = new Date();
this.route.queryParamMap.subscribe(queryParamMap => {
this.barCode = queryParamMap.get('barCode');
this.cardNum = queryParamMap.get('cardNum');
this.initials = queryParamMap.get('initials').toUpperCase();
});
this.settings = this.settingsService.getSettings();
@ -70,11 +70,12 @@ export class PrintComponent implements AfterViewInit {
styleEl.setAttribute('type', 'text/css');
styleEl.appendChild(document.createTextNode(printCss));
headEl.appendChild(styleEl);
this.changeDetector.detectChanges();
}
save(callback: (s: Sample) => void) {
const id = createQrCodeValue(
this.barCode,
this.cardNum,
this.initials,
this.dateCreated,
this.settings.locationId,
@ -84,7 +85,7 @@ export class PrintComponent implements AfterViewInit {
const newSample: Sample = {
barcode: id,
student_id: this.barCode,
student_id: this.cardNum,
date: this.dateCreated,
location: this.settings.locationId,
};

View File

@ -10,18 +10,18 @@
</mat-card-header>
<mat-card-content fxLayout="row wrap" fxLayoutAlign="center center">
<mat-form-field class="barcode-input" fxFlex="95%">
<mat-form-field class="cardnum-input" fxFlex="95%">
<mat-label>Barcode ID #</mat-label>
<input
#barCodeInput="matInput"
#cardNumInput="matInput"
matInput
type="text"
[formControl]="barCodeFormControl"
(change)="checkBarCodeValue()"
[formControl]="cardNumFormControl"
(change)="checkCardNumValue()"
(keyup.enter)="goPrint()"
>
<mat-error *ngIf="barCodeFormControl.hasError('required')">This field is required.</mat-error>
<mat-error *ngIf="barCodeFormControl.hasError('pattern')">Please enter exactly 9 or 14 digits.</mat-error>
<mat-error *ngIf="cardNumFormControl.hasError('required')">This field is required.</mat-error>
<mat-error *ngIf="cardNumFormControl.hasError('pattern')">Please enter exactly 9 or 14 digits.</mat-error>
</mat-form-field>
<mat-form-field class="initials-input" fxFlex="95%">
<mat-label>Initials or 6-character UVA Computing ID</mat-label>
@ -39,6 +39,7 @@
</mat-card-content>
<mat-card-actions>
<button
id="btn_next"
#nextButton="matButton"
mat-flat-button
class="btn-lg"

View File

@ -1,2 +0,0 @@
.barcode-input {
}

View File

@ -18,9 +18,9 @@ import {SettingsService} from '../services/settings.service';
})
export class SampleComponent implements OnInit, AfterViewInit {
settings: AppDefaults;
barCodeFormControl: FormControl;
cardNumFormControl: FormControl;
initialsFormControl: FormControl;
@ViewChild('barCodeInput') barCodeInput: MatInput;
@ViewChild('cardNumInput') cardNumInput: MatInput;
@ViewChild('initialsInput') initialsInput: MatInput;
@ViewChild('nextButton') nextButton: MatButton;
@ -32,35 +32,35 @@ export class SampleComponent implements OnInit, AfterViewInit {
private apiService: ApiService
) {
this.settings = this.settingsService.getSettings();
this.barCodeFormControl = new FormControl('', [
this.cardNumFormControl = new FormControl('', [
Validators.required,
Validators.pattern(this.settings.barCodeRegExp),
Validators.pattern(this.settings.cardNumRegExp),
]);
this.initialsFormControl = new FormControl('', [
Validators.required,
Validators.pattern(this.settings.initialsRegExp),
]);
this.barCodeFormControl.registerOnChange(() => this.checkBarCodeValue());
this.cardNumFormControl.registerOnChange(() => this.checkCardNumValue());
this.initialsFormControl.registerOnChange(() => this.checkInitialsValue());
}
get queryParams(): Params {
return {
barCode: this.barCodeValue.slice(0, 9),
cardNum: this.cardNumValue.slice(0, 9),
initials: this.initialsValue.toUpperCase(),
};
}
get barCodeValue(): string {
return this.barCodeFormControl.value;
get cardNumValue(): string {
return this.cardNumFormControl.value;
}
get initialsValue(): string {
return this.initialsFormControl.value.toUpperCase();
}
get hasBarCode(): boolean {
return this.settings.barCodeRegExp.test(this.barCodeValue);
get hasCardNum(): boolean {
return this.settings.cardNumRegExp.test(this.cardNumValue);
}
get hasInitials(): boolean {
@ -68,7 +68,7 @@ export class SampleComponent implements OnInit, AfterViewInit {
}
get hasErrors(): boolean {
return !(this.barCodeFormControl.valid && this.initialsFormControl.valid);
return !(this.cardNumFormControl.valid && this.initialsFormControl.valid);
}
ngOnInit(): void {
@ -95,26 +95,26 @@ export class SampleComponent implements OnInit, AfterViewInit {
}
ngAfterViewInit() {
this.barCodeInput.focus();
this.cardNumInput.focus();
this.changeDetector.detectChanges();
}
checkBarCodeValue() {
if (this.hasBarCode) {
checkCardNumValue() {
if (this.hasCardNum) {
this.initialsInput.focus();
this.changeDetector.detectChanges();
}
}
checkInitialsValue() {
if (this.hasInitials && this.hasBarCode) {
if (this.hasInitials && this.hasCardNum) {
this.nextButton.focus();
this.changeDetector.detectChanges();
}
}
resetForm() {
this.barCodeFormControl.patchValue('');
this.cardNumFormControl.patchValue('');
this.initialsFormControl.patchValue('');
}

View File

@ -3,7 +3,7 @@ import {HttpClient} from '@angular/common/http';
import {Inject, Injectable} from '@angular/core';
import {Router} from '@angular/router';
import {Observable, throwError} from 'rxjs';
import {catchError} from 'rxjs/operators';
import {catchError, timeout} from 'rxjs/operators';
import {ApiError} from '../models/apiError.interface';
import {AppEnvironment} from '../models/appEnvironment.interface';
import {Sample} from '../models/sample.interface';
@ -39,6 +39,7 @@ export class ApiService {
return this.httpClient
.post<null>(url, sample)
.pipe(timeout(1000), catchError(err => this._handleError(err)))
.pipe(catchError(err => this._handleError(err)));
}

View File

@ -10,7 +10,7 @@
</mat-card-header>
<mat-card-content fxLayout="row wrap" fxLayoutAlign="center center">
<mat-form-field class="location-input" fxFlex="95%" >
<mat-form-field [ngClass]="'location-input'" fxFlex="95%" >
<mat-label>Location ID #</mat-label>
<input
#locationIdInput="matInput"
@ -28,14 +28,18 @@
#labelLayoutSelect="matSelect"
[formControl]="labelLayoutFormControl"
>
<mat-select-trigger>{{selectedLabelLayout.name}}</mat-select-trigger>
<mat-select-trigger [ngClass]="'selected-label-layout'">{{selectedLabelLayout.name}}</mat-select-trigger>
<mat-option
*ngFor="let layout of labelLayouts"
[value]="layout.id"
[ngStyle]="{height: 'inherit', width: 'inherit'}"
>
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="40px">
<div fxFlex="50%" [ngStyle]="{whiteSpace: 'normal', lineHeight: '1.2'}">
<div
fxFlex="50%"
[ngStyle]="{whiteSpace: 'normal', lineHeight: '1.2'}"
class="label-layout-name"
>
{{layout.name}}
</div>
<div fxFlex="50%">
@ -65,6 +69,7 @@
</mat-card-content>
<mat-card-actions>
<button
id="btn_save"
mat-flat-button
class="btn-xl"
color="accent"