diff --git a/.travis.yml b/.travis.yml index fdbcecd..71d6790 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,6 @@ env: - BASE_HREF=/ - DEPLOY_URL=/ - HOME_ROUTE=home - - IRB_URL=http://localhost:5001/ - PORT0=4200 - PRODUCTION=false script: @@ -42,7 +41,7 @@ script: deploy: provider: script - script: bash ./deploy.sh sartography/cr-connect-frontend + script: bash ./deploy.sh sartography/uva-covid19-testing-kiosk on: all_branches: true condition: $TRAVIS_BRANCH =~ ^(dev|testing|demo|training|staging|master|rrt\/.*)$ diff --git a/package-lock.json b/package-lock.json index 555051d..0777884 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3790,6 +3790,15 @@ "requires": { "p-try": "^2.0.0" } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } } } }, @@ -10404,7 +10413,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, "requires": { "safe-buffer": "^5.1.0" } @@ -10951,8 +10959,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safe-regex": { "version": "1.1.0", @@ -11165,10 +11172,9 @@ } }, "serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "requires": { "randombytes": "^2.1.0" } @@ -12351,6 +12357,15 @@ "p-try": "^2.0.0" } }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -13499,6 +13514,17 @@ "terser": "^4.1.2", "webpack-sources": "^1.4.0", "worker-farm": "^1.7.0" + }, + "dependencies": { + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + } } }, "to-regex-range": { diff --git a/package.json b/package.json index 107c4f3..99af80a 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "ngx-qrcode-svg": "^2.0.0", "rfdc": "^1.1.4", "rxjs": "~6.6.3", + "serialize-javascript": "^5.0.1", "tslib": "^2.0.0", "zone.js": "~0.10.3" }, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 76fb535..2a24fc7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -5,10 +5,12 @@ import {FlexLayoutModule} from '@angular/flex-layout'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; +import {MatOptionModule} from '@angular/material/core'; import {MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldModule} from '@angular/material/form-field'; import {MatIconModule} from '@angular/material/icon'; import {MatInputModule} from '@angular/material/input'; import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import {MatSelectModule} from '@angular/material/select'; import {MatToolbarModule} from '@angular/material/toolbar'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {FormlyModule} from '@ngx-formly/core'; @@ -70,7 +72,10 @@ export function getBaseHref(platformLocation: PlatformLocation): string { MatIconModule, MatFormFieldModule, QRCodeSVGModule, - AppRoutingModule, // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters) + AppRoutingModule, + MatOptionModule, + MatSelectModule, + // <-- This line MUST be last (https://angular.io/guide/router#module-import-order-matters) ], providers: [ {provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, useValue: {appearance: 'outline'}}, diff --git a/src/app/config/defaults.ts b/src/app/config/defaults.ts index 88a3e5b..d343455 100644 --- a/src/app/config/defaults.ts +++ b/src/app/config/defaults.ts @@ -1,31 +1,60 @@ -import {AppDefaults} from '../interfaces/appDefaults.interface'; -import {LabelLayout, LabelLayoutType} from '../interfaces/labelLayout'; - -// Default form field and data values -export const defaults: AppDefaults = { - countsCollection: 'counts', // Name of collection for Line Counts in Firebase. - samplesCollection: 'samples', // Name of collection for Line Counts in Firebase. - dateEncodedFormat: 'yyyyMMddHHmm', // Format for dates when encoded in IDs for database records. - dateDisplayFormat: 'MM/dd/yyyy, hh:mm aa', // Format for dates when displayed to user. - numCopies: 3, // Default number of copies of labels to print. Can be overridden by user setting. - labelLayout: 'round_32mm_1up' as LabelLayoutType, // Which label layout to use for printing. Can be overridden by user setting. - locationId: '0000', // Default location ID. Can be overridden by user setting. - lineCountRegex: /^[\d]{4}-[\d]{12}$/, // ID format for Line Count records. - qrCodeRegex: /^[\d]{9}-[a-zA-Z]+-[\d]{12}-[\d]{4}$/, // ID format for QR Code records. - barCodeNumLength: 9, // Number of digits in Bar Code. - barCodeRegex: /^[\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. - initialsLength: 5, - initialsRegex: /^[a-zA-Z]{2,5}$/, -}; +import createClone from 'rfdc'; +import serializeJs from 'serialize-javascript'; +import {AppDefaults, AppDefaultsOptions} from '../interfaces/appDefaults.interface'; +import {LabelLayout} from '../interfaces/labelLayout.interface'; export const labelLayouts = { round_32mm_1up: new LabelLayout({ + name: '32mm Round Label - 1up', + type: 'round_32mm_1up', numCols: 1, columnGap: 0, }), round_32mm_2up: new LabelLayout({ + name: '32mm Round Label - 2up', + type: 'round_32mm_2up', numCols: 2, columnGap: 1.3, }), }; + +// Default form field and data values +export const defaults: AppDefaults = new AppDefaults({ + barCodeNumLength: 9, // Number of digits in Bar Code. + barCodeRegExp: /^[\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. + dateDisplayFormat: 'MM/dd/yyyy, hh:mm aa', // Format for dates when displayed to user. + dateEncodedFormat: 'yyyyMMddHHmm', // Format for dates when encoded in IDs for database records. + initialsLength: 5, + initialsRegExp: /^[a-zA-Z]{2,5}$/, + labelLayout: labelLayouts.round_32mm_1up, // Which label layout to use for printing. Can be overridden by user setting. + lineCountRegExp: /^[\d]{4}-[\d]{12}$/, // ID format for Line Count records. + locationId: '0000', // Default location ID. Can be overridden by user setting. + locationIdRegExp: /^[\d]{4}$/, // ID format for Line Count records. + numCopies: 1, // Default number of copies of labels to print. Can be overridden by user setting. + qrCodeRegExp: /^[\d]{9}-[a-zA-Z]+-[\d]{12}-[\d]{4}$/, // ID format for QR Code records. + samplesCollection: 'samples', // Name of collection for Line Counts in Firebase. +}); + +export const getSettings = (): AppDefaults => { + const storedSettings = localStorage.getItem('settings'); + + if (storedSettings) { + // tslint:disable-next-line:no-eval + return new AppDefaults(eval(`(${storedSettings})`)); + } else { + localStorage.setItem('settings', serializeJs(defaults)); + return defaults; + } +}; + +export const saveSettings = (newSettings: AppDefaultsOptions): AppDefaults => { + const settings: AppDefaults = createClone()(getSettings()); + + Object.keys(newSettings).forEach(k => { + settings[k] = newSettings[k]; + }); + + localStorage.setItem('settings', serializeJs(settings)); + return getSettings(); +}; diff --git a/src/app/interfaces/appDefaults.interface.ts b/src/app/interfaces/appDefaults.interface.ts index 1623dd0..751d9c5 100644 --- a/src/app/interfaces/appDefaults.interface.ts +++ b/src/app/interfaces/appDefaults.interface.ts @@ -1,17 +1,51 @@ -import {LabelLayoutType} from './labelLayout'; +import {LabelLayout} from './labelLayout.interface'; -export interface AppDefaults { - countsCollection: string; - samplesCollection: string; - dateEncodedFormat: string; - dateDisplayFormat: string; - numCopies: number; - labelLayout: LabelLayoutType; - locationId: string; - lineCountRegex: RegExp; - qrCodeRegex: RegExp; - barCodeRegex: RegExp; - barCodeNumLength: number; - initialsRegex: RegExp; - initialsLength: number; +export interface AppDefaultsOptions { + barCodeNumLength?: number; + barCodeRegExp?: RegExp | string; + countsCollection?: string; + dateDisplayFormat?: string; + dateEncodedFormat?: string; + initialsLength?: number; + initialsRegExp?: RegExp | string; + labelLayout?: LabelLayout; + lineCountRegExp?: RegExp | string; + locationId?: string; + locationIdRegExp?: RegExp | string; + numCopies?: number; + qrCodeRegExp?: RegExp | string; + samplesCollection?: string; +} + +export class AppDefaults { + barCodeNumLength: number; + barCodeRegExp: RegExp; + countsCollection: string; + dateDisplayFormat: string; + dateEncodedFormat: string; + initialsLength: number; + initialsRegExp: RegExp; + labelLayout: LabelLayout; + lineCountRegExp: RegExp; + locationId: string; + locationIdRegExp: RegExp; + numCopies: number; + qrCodeRegExp: RegExp; + samplesCollection: string; + + constructor(options: AppDefaultsOptions) { + console.log('options', options); + const keys = Object.keys(options); + keys.forEach(k => { + if (k.includes('RegExp')) { + if (typeof options[k] === 'string') { + this[k] = new RegExp(options[k]); + } else { + this[k] = options[k]; + } + } else { + this[k] = options[k]; + } + }); + } } diff --git a/src/app/interfaces/labelLayout.ts b/src/app/interfaces/labelLayout.interface.ts similarity index 70% rename from src/app/interfaces/labelLayout.ts rename to src/app/interfaces/labelLayout.interface.ts index 68ba560..0aa9ffc 100644 --- a/src/app/interfaces/labelLayout.ts +++ b/src/app/interfaces/labelLayout.interface.ts @@ -1,6 +1,6 @@ -export declare type LabelLayoutType = 'round_32mm_1up' | 'round_32mm_2up'; - export interface LayoutOptions { + type?: string; + name?: string; units?: string; pointsPerUnit?: number; labelSize?: number; @@ -17,6 +17,8 @@ export interface LayoutOptions { } export class LabelLayout { + type = 'round_32mm_1up'; + name = '32mm Round Label - 1up'; units = 'mm'; pointsPerUnit = 0.3528; labelSize = 28.6; @@ -32,18 +34,10 @@ export class LabelLayout { numCopies = 1; constructor(private options: LayoutOptions) { - this.units = options.units || this.units; - this.pointsPerUnit = options.pointsPerUnit || this.pointsPerUnit; - this.marginSize = options.marginSize || this.marginSize; - this.labelSize = options.labelSize || this.labelSize; - this.numCols = options.numCols || this.numCols; - this.columnGap = options.columnGap || this.columnGap; - this.sideTextWidth = options.sideTextWidth || this.sideTextWidth; - this.sideTextTop = options.sideTextTop || this.sideTextTop; - this.sideTextMargin = options.sideTextMargin || this.sideTextMargin; - this.topTextMargin = options.topTextMargin || this.topTextMargin; - this.bottomTextMargin = options.bottomTextMargin || this.bottomTextMargin; - this.fontSizePt = options.fontSizePt || this.fontSizePt; + const keys = Object.keys(options); + keys.forEach(k => { + this[k] = options[k]; + }); } get dimensions() { diff --git a/src/app/print/print.component.ts b/src/app/print/print.component.ts index 97987b5..5da95cb 100644 --- a/src/app/print/print.component.ts +++ b/src/app/print/print.component.ts @@ -43,7 +43,7 @@ export class PrintComponent implements OnInit { } get columns() { - return Array(defaults.labelLayout === 'round_32mm_2up' ? 2 : 1).fill(''); + return Array(defaults.labelLayout.type === 'round_32mm_2up' ? 2 : 1).fill(''); } ngOnInit(): void { diff --git a/src/app/sample/sample.component.html b/src/app/sample/sample.component.html index 12f4d9e..031e0d3 100644 --- a/src/app/sample/sample.component.html +++ b/src/app/sample/sample.component.html @@ -18,16 +18,6 @@ type="text" [formControl]="barCodeFormControl" > - This field is required. Please enter exactly 9 or 14 digits. @@ -39,31 +29,30 @@ type="text" [formControl]="initialsFormControl" > - This field is required. - Please enter only letters. + Please enter only 2-5 letters. - + >Next navigate_next + + diff --git a/src/app/sample/sample.component.ts b/src/app/sample/sample.component.ts index 207d96a..88b22ab 100644 --- a/src/app/sample/sample.component.ts +++ b/src/app/sample/sample.component.ts @@ -1,8 +1,10 @@ import {AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core'; import {FormControl, Validators} from '@angular/forms'; +import {MatButton} from '@angular/material/button'; import {MatInput} from '@angular/material/input'; import {Params} from '@angular/router'; -import {defaults} from '../config/defaults'; +import {defaults, getSettings} from '../config/defaults'; +import {AppDefaults} from '../interfaces/appDefaults.interface'; @Component({ @@ -11,18 +13,18 @@ import {defaults} from '../config/defaults'; styleUrls: ['./sample.component.scss'] }) export class SampleComponent implements AfterViewInit { - barCodeErrorMessage = ''; - initialsErrorMessage = ''; + settings: AppDefaults = getSettings(); barCodeFormControl = new FormControl('', [ Validators.required, - Validators.pattern(defaults.barCodeRegex), + Validators.pattern(this.settings.barCodeRegExp), ]); initialsFormControl = new FormControl('', [ Validators.required, - Validators.pattern(defaults.initialsRegex), + Validators.pattern(this.settings.initialsRegExp), ]); @ViewChild('barCodeInput') barCodeInput: MatInput; @ViewChild('initialsInput') initialsInput: MatInput; + @ViewChild('nextButton') nextButton: MatButton; get queryParams(): Params { return { @@ -32,9 +34,7 @@ export class SampleComponent implements AfterViewInit { } constructor(private changeDetector: ChangeDetectorRef) { - this.barCodeFormControl.registerOnChange(() => { - this.checkBarCodeValue(); - }); + this.barCodeFormControl.registerOnChange(() => this.checkBarCodeValue()); this.initialsFormControl.registerOnChange(() => this.checkInitialsValue()); } @@ -47,11 +47,11 @@ export class SampleComponent implements AfterViewInit { } get hasBarCode(): boolean { - return defaults.barCodeRegex.test(this.barCodeValue); + return this.settings.barCodeRegExp.test(this.barCodeValue); } get hasInitials(): boolean { - return defaults.initialsRegex.test(this.initialsValue); + return this.settings.initialsRegExp.test(this.initialsValue); } get hasInfo(): boolean { @@ -66,19 +66,21 @@ export class SampleComponent implements AfterViewInit { checkBarCodeValue() { console.log('--- checkBarCodeValue ---'); if (this.hasBarCode) { - this.barCodeErrorMessage = ''; this.initialsInput.focus(); this.changeDetector.detectChanges(); - } else { - this.barCodeErrorMessage = 'Wrong barcode.'; } } checkInitialsValue() { - if (this.hasInitials) { - this.initialsErrorMessage = ''; - } else { - this.initialsErrorMessage = 'Wrong barcode.'; + console.log('--- checkInitialsValue ---'); + if (this.hasInitials && this.hasBarCode) { + this.nextButton.focus(); + this.changeDetector.detectChanges(); } } + + resetForm() { + this.barCodeFormControl.patchValue(''); + this.initialsFormControl.patchValue(''); + } } diff --git a/src/app/settings/settings.component.html b/src/app/settings/settings.component.html index 4ab2a41..d85d9d1 100644 --- a/src/app/settings/settings.component.html +++ b/src/app/settings/settings.component.html @@ -1 +1,64 @@ -

settings works!

+
+ + +

Scan Barcode

+
+ + + + Location ID # + + This field is required. + Please enter exactly 4 digits. + + + Label layout + + {{layout.name}} + + This field is required. + + + Number of copies of label + + This field is required. + Please enter only 2-5 letters. + + + + + + +
+ +
diff --git a/src/app/settings/settings.component.ts b/src/app/settings/settings.component.ts index 232b5dd..ad1f90d 100644 --- a/src/app/settings/settings.component.ts +++ b/src/app/settings/settings.component.ts @@ -1,4 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; +import {FormControl, Validators} from '@angular/forms'; +import {MatInput} from '@angular/material/input'; +import {MatSelect} from '@angular/material/select'; +import {getSettings, labelLayouts, saveSettings} from '../config/defaults'; +import {AppDefaults} from '../interfaces/appDefaults.interface'; +import {LabelLayout} from '../interfaces/labelLayout.interface'; + @Component({ selector: 'app-settings', @@ -6,10 +13,33 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./settings.component.scss'] }) export class SettingsComponent implements OnInit { + settings: AppDefaults = getSettings(); + numCopiesFormControl = new FormControl(this.settings.numCopies, [ + Validators.required, + ]); + labelLayoutFormControl = new FormControl(this.settings.labelLayout.type, [ + Validators.required, + ]); + locationIdFormControl = new FormControl(this.settings.locationId, [ + Validators.required, + Validators.pattern(this.settings.locationIdRegExp), + ]); - constructor() { } + labelLayouts: LabelLayout[] = Object.values(labelLayouts); + + constructor() { + } + + get hasInfo(): boolean { + return this.numCopiesFormControl.valid && this.locationIdFormControl.valid; + } ngOnInit(): void { } + save() { + saveSettings({ + + }); + } } diff --git a/src/polyfills.ts b/src/polyfills.ts index aa665d6..e33d26d 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -61,3 +61,4 @@ import 'zone.js/dist/zone'; // Included with Angular CLI. /*************************************************************************************************** * APPLICATION IMPORTS */ +(window as any).global = window;