Replaces home screen with sample screen. Goes back to sample screen when print button clicked. Forces initials to uppercase. Tweaks label formatting. Displays locationId in navbar.

This commit is contained in:
Aaron Louie 2020-11-13 12:50:33 -05:00
parent ae9c484cf0
commit f1d93684a9
20 changed files with 101 additions and 96 deletions

View File

@ -10,9 +10,10 @@ describe('COVID19 Testing Kiosk App', () => {
http = new HttpClient('http://localhost:5001');
});
it('should automatically sign-in and redirect to home screen', () => {
it('should automatically sign-in and redirect to sample input screen', () => {
page.navigateTo();
expect(page.getRoute()).toEqual('/');
});
it('should navigate to settings screen', () => {
@ -23,15 +24,7 @@ describe('COVID19 Testing Kiosk App', () => {
page.clickAndExpectRoute('#nav_home', '/');
});
it('should navigate to occupancy count input screen', () => {
page.clickAndExpectRoute('#nav_count', '/count');
});
it('should navigate back to home screen', () => {
it('should navigate back to sample input screen', () => {
page.clickAndExpectRoute('#nav_home', '/');
});
it('should navigate to sample input screen', () => {
page.clickAndExpectRoute('#nav_sample', '/sample');
});
});

View File

@ -11,7 +11,7 @@ export const routes: Routes = [
{
path: '',
pathMatch: 'full',
component: HomeComponent
component: SampleComponent
},
{
path: 'sample',

View File

@ -1,5 +1,5 @@
<div class="mat-typography">
<app-navbar [testingLocation]="testingLocation"></app-navbar>
<app-navbar></app-navbar>
<router-outlet *ngIf="!loading; else loadingMessage"></router-outlet>
<app-footer></app-footer>
</div>

View File

@ -1,22 +1,15 @@
import {Component, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material/icon';
import {DomSanitizer, Title} from '@angular/platform-browser';
import {Router} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {AppEnvironment} from './models/appEnvironment.interface';
import {TestingLocation} from './models/testingLocation.interface';
import {GoogleAnalyticsService} from './services/google-analytics.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
loading: boolean;
testingLocation: TestingLocation = {
id: '0000',
name: 'Click here to set location',
};
constructor(
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
@ -25,6 +18,7 @@ export class AppComponent {
this.titleService.setTitle(this.environment.title);
}
reload() {
this.loading = true;
setTimeout(() => this.loading = false, 300);

View File

@ -20,7 +20,7 @@ describe('BarcodeDataMatrixComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(BarcodeDataMatrixComponent);
component = fixture.componentInstance;
settings.labelLayout = labelLayouts.rectangular_lg;
// settings.labelLayout = labelLayouts.rectangular_lg;
component.settings = settings;
component.format = settings.labelLayout.barcodeType;
component.value = '987654321-202101231122-ABCDE-0123';

View File

@ -26,31 +26,31 @@ export const labelLayouts = {
topTextMargin: 0,
bottomTextMargin: 0,
}),
rectangular_lg: new LabelLayout({
name: '2in x 1.25in Rectangular Label',
barcodeType: 'qrcode',
type: 'rectangular_lg',
numCols: 1,
columnGap: 0,
labelWidth: 28.6,
labelHeight: 28.6,
sideTextMargin: 10,
topTextMargin: 1,
bottomTextMargin: 1,
}),
rectangular_sm: new LabelLayout({
name: '96mm x 15mm Rectangular Label',
barcodeType: 'datamatrix',
type: 'rectangular_sm',
numCols: 1,
columnGap: 0,
labelWidth: 32,
labelHeight: 16,
marginSize: 6,
sideTextMargin: 2,
topTextMargin: 3,
bottomTextMargin: 3,
}),
// rectangular_lg: new LabelLayout({
// name: '2in x 1.25in Rectangular Label',
// barcodeType: 'qrcode',
// type: 'rectangular_lg',
// numCols: 1,
// columnGap: 0,
// labelWidth: 28.6,
// labelHeight: 28.6,
// sideTextMargin: 10,
// topTextMargin: 1,
// bottomTextMargin: 1,
// }),
// rectangular_sm: new LabelLayout({
// name: '96mm x 15mm Rectangular Label',
// barcodeType: 'datamatrix',
// type: 'rectangular_sm',
// numCols: 1,
// columnGap: 0,
// labelWidth: 32,
// labelHeight: 16,
// marginSize: 6,
// sideTextMargin: 2,
// topTextMargin: 3,
// bottomTextMargin: 3,
// }),
};
export const defaultOptions: AppDefaultsOptions = {

View File

@ -5,7 +5,7 @@
<div
class="date"
[ngStyle]="{marginTop: settings.labelLayout.dimensions.topTextMargin}"
>{{dateCreated | date:'yyMMdd'}}{{initials}}</div>
>{{dateCreated | date:'yyMMdd'}}{{initials.toUpperCase()}}</div>
<div
class="time"
[ngStyle]="{marginLeft: settings.labelLayout.dimensions.sideTextMargin}"

View File

@ -14,18 +14,22 @@
width: 24.6mm;
height: 24.6mm;
border: 2mm solid transparent;
border-radius: 2mm;
background-color: transparent;
border-radius: 100%;
background-color: white;
padding: 0;
margin: 0;
.date {
top: 0;
left: 0;
right: 0;
width: 100%;
}
.barcode {
bottom: 0;
left: 0;
right: 0;
width: 100%;
}

View File

@ -38,7 +38,7 @@ export class LabelLayoutComponent implements OnInit {
get qrCodeValue(): string {
return createQrCodeValue(
this.barCode,
this.initials,
this.initials.toUpperCase(),
this.dateCreated,
this.settings.locationId
);

View File

@ -28,12 +28,12 @@ export class LabelLayout {
labelWidth = 28.6;
marginSize = 1.7;
numCols = 1;
columnGap = 4;
columnGap = 0;
sideTextWidth = 4;
sideTextTop = 11;
sideTextMargin = 1.5;
topTextMargin = 3;
bottomTextMargin = 2.5;
sideTextMargin = 0;
topTextMargin = 0;
bottomTextMargin = 0;
fontSizePt = 6;
numCopies = 1;

View File

@ -1,6 +1,6 @@
import {Component, Input, OnInit} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {AppDefaults} from '../models/appDefaults.interface';
import {SettingsService} from '../services/settings.service';
import {TestingLocation} from '../models/testingLocation.interface';
@Component({
selector: 'app-navbar',
@ -8,16 +8,20 @@ import {TestingLocation} from '../models/testingLocation.interface';
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit {
@Input() testingLocation: TestingLocation;
settings: AppDefaults;
locationId: string;
constructor(private settingsService: SettingsService) {
this.settingsService.settings.subscribe(settings => {
this.settings = settings;
this.locationId = !this.hasDefaultId ? this.settings.locationId : '0000';
});
}
get hasDefaultId(): boolean {
return (!this.settings || this.settings.locationId === '0000');
}
ngOnInit(): void {
}
get locationId(): string {
const settings = this.settingsService.getSettings();
return settings.locationId;
}
}

View File

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

View File

@ -16,6 +16,7 @@ describe('PrintLayoutComponent', () => {
beforeEach(() => {
fixture = TestBed.createComponent(PrintLayoutComponent);
component = fixture.componentInstance;
component.initials = 'abcde';
fixture.detectChanges();
});

View File

@ -17,7 +17,7 @@
>
<app-print-layout
[barCode]="barCode"
[initials]="initials"
[initials]="initials.toUpperCase()"
[dateCreated]="dateCreated"
></app-print-layout>
</mat-card-content>
@ -26,7 +26,7 @@
#saveAndPrintButton="matButton"
mat-flat-button
class="btn-lg"
[color]="isSaved ? '' : 'accent'"
color="accent"
(click)="saveAndPrint()"
autofocus
fxFlex="33%"
@ -37,8 +37,7 @@
class="btn-lg"
routerLink="/"
fxFlex="33%"
[color]="isSaved ? 'accent' : ''"
><mat-icon>done</mat-icon> Done</button>
><mat-icon>cancel</mat-icon> Cancel</button>
</mat-card-actions>
</mat-card>
</div>
@ -46,7 +45,7 @@
<div id="media-print">
<app-print-layout
[barCode]="barCode"
[initials]="initials"
[initials]="initials.toUpperCase()"
[dateCreated]="dateCreated"
></app-print-layout>
</div>

View File

@ -1,6 +1,6 @@
import {AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
import {MatButton} from '@angular/material/button';
import {ActivatedRoute} from '@angular/router';
import {ActivatedRoute, Router} from '@angular/router';
import {createQrCodeValue} from '../_util/qrCode';
import {AppDefaults} from '../models/appDefaults.interface';
import {LabelLayout} from '../models/labelLayout.interface';
@ -26,6 +26,7 @@ export class PrintComponent implements AfterViewInit {
constructor(
private api: ApiService,
private route: ActivatedRoute,
private router: Router,
private changeDetector: ChangeDetectorRef,
private settingsService: SettingsService,
private cacheService: CacheService,
@ -33,13 +34,14 @@ export class PrintComponent implements AfterViewInit {
this.dateCreated = new Date();
this.route.queryParamMap.subscribe(queryParamMap => {
this.barCode = queryParamMap.get('barCode');
this.initials = queryParamMap.get('initials');
this.initials = queryParamMap.get('initials').toUpperCase();
});
this.settings = this.settingsService.getSettings();
this.isSaved = false;
this.save(s => {
this.isSaved = true;
this.changeDetector.detectChanges();
});
}
@ -73,7 +75,7 @@ export class PrintComponent implements AfterViewInit {
save(callback: (s: Sample) => void) {
const id = createQrCodeValue(
this.barCode,
this.initials,
this.initials.toUpperCase(),
this.dateCreated,
this.settings.locationId
);
@ -102,8 +104,7 @@ export class PrintComponent implements AfterViewInit {
saveAndPrint() {
this.save(s => {
window.print();
this.doneButton.focus();
this.changeDetector.detectChanges();
this.router.navigate(['/']);
});
}
}

View File

@ -45,20 +45,14 @@
color="accent"
[disabled]="hasErrors"
(click)="goPrint()"
fxFlex="33%"
fxFlex="50%"
>Next <mat-icon>navigate_next</mat-icon></button>
<button
mat-flat-button
class="btn-lg"
(click)="resetForm()"
fxFlex="33%"
fxFlex="50%"
><mat-icon>restore</mat-icon> Reset</button>
<button
mat-flat-button
class="btn-lg"
routerLink="/"
fxFlex="33%"
><mat-icon>cancel</mat-icon> Cancel</button>
</mat-card-actions>
</mat-card>

View File

@ -41,7 +41,7 @@ export class SampleComponent implements AfterViewInit {
get queryParams(): Params {
return {
barCode: this.barCodeValue.slice(0, 9),
initials: this.initialsValue,
initials: this.initialsValue.toUpperCase(),
};
}
@ -50,7 +50,7 @@ export class SampleComponent implements AfterViewInit {
}
get initialsValue(): string {
return this.initialsFormControl.value;
return this.initialsFormControl.value.toUpperCase();
}
get hasBarCode(): boolean {
@ -58,7 +58,7 @@ export class SampleComponent implements AfterViewInit {
}
get hasInitials(): boolean {
return this.settings.initialsRegExp.test(this.initialsValue);
return this.settings.initialsRegExp.test(this.initialsValue.toUpperCase());
}
get hasErrors(): boolean {

View File

@ -1,5 +1,6 @@
import {Injectable} from '@angular/core';
import createClone from 'rfdc';
import {BehaviorSubject} from 'rxjs';
import serializeJs from 'serialize-javascript';
import {defaultOptions} from '../config/defaults';
import {AppDefaults, AppDefaultsOptions} from '../models/appDefaults.interface';
@ -11,10 +12,15 @@ export class SettingsService {
// Default form field and data values
defaults: AppDefaults = new AppDefaults(defaultOptions);
// Observable to subscribe to settings updates
settings = new BehaviorSubject<AppDefaults>(this.defaults);
// Deserializes settings from local storage and returns AppDefaults instance
getStoredSettings(): AppDefaults {
// tslint:disable-next-line:no-eval
return new AppDefaults(eval(`(${localStorage.getItem('settings')})`));
const storedSettings = new AppDefaults(eval(`(${localStorage.getItem('settings')})`));
this.settings.next(storedSettings);
return storedSettings;
}
// Returns true if settings are found in local storage
@ -40,6 +46,7 @@ export class SettingsService {
});
localStorage.setItem('settings', serializeJs(settings));
this.settings.next(settings);
return settings;
}

View File

@ -10,13 +10,14 @@
</mat-card-header>
<mat-card-content fxLayout="row wrap" fxLayoutAlign="center center">
<mat-form-field class="location-input" fxFlex="95%">
<mat-form-field class="location-input" fxFlex="95%" >
<mat-label>Location ID #</mat-label>
<input
#locationIdInput="matInput"
matInput
type="text"
[formControl]="locationIdFormControl"
(keyup.enter)="save()"
>
<mat-error *ngIf="locationIdFormControl.hasError('required')">This field is required.</mat-error>
<mat-error *ngIf="locationIdFormControl.hasError('pattern')">Please enter exactly 4 digits.</mat-error>
@ -38,6 +39,7 @@
matInput
type="number"
[formControl]="numCopiesFormControl"
(keyup.enter)="save()"
>
<mat-error *ngIf="numCopiesFormControl.hasError('required')">This field is required.</mat-error>
<mat-error *ngIf="numCopiesFormControl.hasError('pattern')">Please enter only 2-5 letters.</mat-error>

View File

@ -1,5 +1,6 @@
import {Component, OnInit} from '@angular/core';
import {AfterViewInit, Component, OnInit, ViewChild} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
import {MatInput} from '@angular/material/input';
import {Router} from '@angular/router';
import {labelLayouts} from '../config/defaults';
import {AppDefaults} from '../models/appDefaults.interface';
@ -11,13 +12,15 @@ import {SettingsService} from '../services/settings.service';
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss']
})
export class SettingsComponent implements OnInit {
export class SettingsComponent implements AfterViewInit {
settings: AppDefaults;
numCopiesFormControl: FormControl;
labelLayoutFormControl: FormControl;
locationIdFormControl: FormControl;
labelLayouts: LabelLayout[];
@ViewChild('locationIdInput') locationIdInput: MatInput;
constructor(
private router: Router,
private settingsService: SettingsService
@ -43,15 +46,18 @@ export class SettingsComponent implements OnInit {
return this.numCopiesFormControl.valid && this.locationIdFormControl.valid;
}
ngOnInit(): void {
ngAfterViewInit(): void {
this.locationIdInput.focus();
}
save() {
this.settingsService.saveSettings({
labelLayout: labelLayouts[this.labelLayoutFormControl.value],
numCopies: this.numCopiesFormControl.value,
locationId: this.locationIdFormControl.value,
});
this.router.navigate(['/']);
if (this.hasInfo) {
this.settingsService.saveSettings({
labelLayout: labelLayouts[this.labelLayoutFormControl.value],
numCopies: this.numCopiesFormControl.value,
locationId: this.locationIdFormControl.value,
});
this.router.navigate(['/']);
}
}
}