Reduces timeout to 1second. Tweaks 1D barcode design. Prints multiple labels.
This commit is contained in:
parent
d5dbc6d310
commit
fa58a9c7d6
|
@ -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/',
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 |
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
>
|
||||
<app-label-layout
|
||||
*ngFor="let col of columns"
|
||||
[barCode]="barCode"
|
||||
[cardNum]="cardNum"
|
||||
[initials]="initials.toUpperCase()"
|
||||
[dateCreated]="dateCreated"
|
||||
></app-label-layout>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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'}))}
|
||||
},
|
||||
]
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
.barcode-input {
|
||||
}
|
|
@ -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('');
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue