Shortens data stored in CODE128. Increases length of initials to accommodate UVA computing ids. Removes old home component and syncs data on Scan Barcode screen.
This commit is contained in:
parent
e2841e3352
commit
82974d2d7a
|
@ -1,11 +1,22 @@
|
|||
import {formatDate} from '@angular/common';
|
||||
|
||||
export const createQrCodeValue = (barCode: string, initials: string, dateCreated: Date, locationId: string): string => {
|
||||
export const createQrCodeValue = (
|
||||
barCode: string,
|
||||
initials: string,
|
||||
dateCreated: Date,
|
||||
locationId: string,
|
||||
delimiter = '-',
|
||||
barcodeType: string
|
||||
): string => {
|
||||
const is1D = barcodeType === 'code128';
|
||||
const locId = is1D ? locationId.slice(2, 4) : locationId;
|
||||
const dateFormat = is1D ? 'yyMMdd' : 'yyyyMMddHHmm';
|
||||
|
||||
const valArray = [
|
||||
barCode,
|
||||
initials.toUpperCase(),
|
||||
formatDate(dateCreated, 'yyyyMMddHHmm', 'en-us'),
|
||||
locationId,
|
||||
formatDate(dateCreated, dateFormat, 'en-us'),
|
||||
locId,
|
||||
];
|
||||
return valArray.join('-');
|
||||
return valArray.join(delimiter);
|
||||
};
|
||||
|
|
|
@ -22,7 +22,6 @@ import {FormlyMaterialModule} from '@ngx-formly/material';
|
|||
import {routes} from './app-routing.module';
|
||||
import {AppComponent} from './app.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {NavbarComponent} from './navbar/navbar.component';
|
||||
import {MockEnvironment} from './testing/environment.mock';
|
||||
|
||||
|
@ -38,7 +37,6 @@ describe('Router: App', () => {
|
|||
declarations: [
|
||||
AppComponent,
|
||||
FooterComponent,
|
||||
HomeComponent,
|
||||
NavbarComponent,
|
||||
],
|
||||
imports: [
|
||||
|
|
|
@ -2,7 +2,6 @@ import {NgModule} from '@angular/core';
|
|||
import {RouterModule, Routes} from '@angular/router';
|
||||
import {ThisEnvironment} from '../environments/environment.injectable';
|
||||
import {CountComponent} from './count/count.component';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {PrintComponent} from './print/print.component';
|
||||
import {SampleComponent} from './sample/sample.component';
|
||||
import {SettingsComponent} from './settings/settings.component';
|
||||
|
|
|
@ -21,7 +21,6 @@ import {AppComponent} from './app.component';
|
|||
import {BarcodeSvgDirective} from './barcode-svg/barcode-svg.directive';
|
||||
import {CountComponent} from './count/count.component';
|
||||
import {FooterComponent} from './footer/footer.component';
|
||||
import {HomeComponent} from './home/home.component';
|
||||
import {CircleQRcodeDoubleComponent} from './label-layout/formats/circle-qrcode-double/circle-qrcode-double.component';
|
||||
import {CircleQRcodeSingleComponent} from './label-layout/formats/circle-qrcode-single/circle-qrcode-single.component';
|
||||
import {RectangleCode128Component} from './label-layout/formats/rectangle-code128/rectangle-code128.component';
|
||||
|
@ -60,7 +59,6 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||
CircleQRcodeSingleComponent,
|
||||
CountComponent,
|
||||
FooterComponent,
|
||||
HomeComponent,
|
||||
LabelLayoutComponent,
|
||||
LoadingComponent,
|
||||
NavbarComponent,
|
||||
|
|
|
@ -22,6 +22,7 @@ export const labelLayouts = {
|
|||
id: 'rectangle_code128',
|
||||
pageWidth: 54,
|
||||
pageHeight: 34,
|
||||
delimiter: '',
|
||||
}),
|
||||
rectangle_datamatrix: new LabelLayout({
|
||||
name: '2in x 1.25in Rectangular Label - DataMatrix',
|
||||
|
@ -41,11 +42,11 @@ export const defaultOptions: AppDefaultsOptions = {
|
|||
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: 6,
|
||||
initialsRegExp: /^[a-zA-Z]{2,6}$/,
|
||||
initialsRegExp: /^[a-zA-Z0-9]{2,6}$/,
|
||||
labelLayout: labelLayouts.circle_qrcode_single, // 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.
|
||||
locationIdRegExp: /^[\d]{1,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.
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
<div
|
||||
class="full-height bg-primary"
|
||||
fxLayout="column"
|
||||
fxLayoutAlign="center center"
|
||||
fxLayoutGap="40px"
|
||||
>
|
||||
<div
|
||||
fxLayout="column"
|
||||
fxLayoutAlign="center center"
|
||||
fxLayoutGap="40px"
|
||||
>
|
||||
<button
|
||||
id="nav_sample"
|
||||
mat-flat-button
|
||||
color="accent"
|
||||
class="btn-xl"
|
||||
routerLink="/sample"
|
||||
autofocus
|
||||
>New saliva sample</button>
|
||||
<button
|
||||
id="nav_count"
|
||||
mat-flat-button
|
||||
class="btn-xl"
|
||||
routerLink="/count"
|
||||
>Submit occupancy count</button>
|
||||
</div>
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
.btn-xl {
|
||||
width: 100%;
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
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<HomeComponent>;
|
||||
let httpMock: HttpTestingController;
|
||||
|
||||
beforeEach(waitForAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
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();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
import {Component, OnInit} from '@angular/core';
|
||||
import {ApiService} from '../services/api.service';
|
||||
import {CacheService} from '../services/cache.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.component.html',
|
||||
styleUrls: ['./home.component.scss']
|
||||
})
|
||||
export class HomeComponent implements OnInit {
|
||||
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -35,10 +35,10 @@
|
|||
<g
|
||||
appBarcodeSvg
|
||||
[format]="'code128'"
|
||||
[height]="10"
|
||||
[height]="40"
|
||||
[value]="sample.barcode"
|
||||
[width]="47"
|
||||
transform="scale(0.345)"/>
|
||||
[width]="188"
|
||||
transform="scale(0.08625)"/>
|
||||
</g>
|
||||
<use xlink:href="#up_arrow" x="2" y="27.5" />
|
||||
</g>
|
||||
|
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
@ -35,7 +35,9 @@ export class LabelLayoutComponent implements OnInit {
|
|||
this.sample.student_id,
|
||||
this.sample.initials,
|
||||
this.sample.date,
|
||||
this.sample.location
|
||||
this.sample.location,
|
||||
this.settings.labelLayout.delimiter,
|
||||
this.settings.labelLayout.barcodeType,
|
||||
);
|
||||
|
||||
this.sample.barcode = this.barcodeValue;
|
||||
|
|
|
@ -7,6 +7,7 @@ export interface LayoutOptions {
|
|||
pageWidth?: number;
|
||||
numCols?: number;
|
||||
numCopies?: number;
|
||||
delimiter?: string;
|
||||
}
|
||||
|
||||
export class LabelLayout {
|
||||
|
@ -18,6 +19,7 @@ export class LabelLayout {
|
|||
pageWidth = 32;
|
||||
numCols = 1;
|
||||
numCopies = 1;
|
||||
delimiter = '-';
|
||||
|
||||
constructor(private options: LayoutOptions) {
|
||||
if (options) {
|
||||
|
|
|
@ -77,7 +77,9 @@ export class PrintComponent implements AfterViewInit {
|
|||
this.barCode,
|
||||
this.initials,
|
||||
this.dateCreated,
|
||||
this.settings.locationId
|
||||
this.settings.locationId,
|
||||
this.settings.labelLayout.delimiter,
|
||||
this.settings.labelLayout.barcodeType,
|
||||
);
|
||||
|
||||
const newSample: Sample = {
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<mat-error *ngIf="barCodeFormControl.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</mat-label>
|
||||
<mat-label>Initials or 6-character UVA Computing ID</mat-label>
|
||||
<input
|
||||
#initialsInput="matInput"
|
||||
matInput
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
|
@ -6,33 +9,46 @@ import {MatIconModule} from '@angular/material/icon';
|
|||
import {MatInputModule} from '@angular/material/input';
|
||||
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {Router} from '@angular/router';
|
||||
|
||||
import { SampleComponent } from './sample.component';
|
||||
import {ApiService} from '../services/api.service';
|
||||
import {CacheService} from '../services/cache.service';
|
||||
import {SettingsService} from '../services/settings.service';
|
||||
import {MockEnvironment} from '../testing/environment.mock';
|
||||
import {SampleComponent} from './sample.component';
|
||||
|
||||
describe('SampleComponent', () => {
|
||||
let component: SampleComponent;
|
||||
let fixture: ComponentFixture<SampleComponent>;
|
||||
const mockRouter = {navigate: jasmine.createSpy('navigate')};
|
||||
let httpMock: HttpTestingController;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SampleComponent ],
|
||||
declarations: [
|
||||
SampleComponent
|
||||
],
|
||||
imports: [
|
||||
HttpClientTestingModule,
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatFormFieldModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
NoopAnimationsModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
providers: [
|
||||
{provide: 'APP_ENVIRONMENT', useClass: MockEnvironment},
|
||||
{provide: APP_BASE_HREF, useValue: '/'},
|
||||
{provide: Router, useValue: mockRouter},
|
||||
ApiService,
|
||||
CacheService,
|
||||
SettingsService,
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
httpMock = TestBed.inject(HttpTestingController);
|
||||
fixture = TestBed.createComponent(SampleComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import {AfterViewInit, ChangeDetectorRef, Component, ViewChild} from '@angular/core';
|
||||
import {APP_BASE_HREF} from '@angular/common';
|
||||
import {AfterViewInit, ChangeDetectorRef, Component, Inject, 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, Router} from '@angular/router';
|
||||
import {AppDefaults} from '../models/appDefaults.interface';
|
||||
import {AppEnvironment} from '../models/appEnvironment.interface';
|
||||
import {ApiService} from '../services/api.service';
|
||||
import {CacheService} from '../services/cache.service';
|
||||
import {SettingsService} from '../services/settings.service';
|
||||
|
||||
|
||||
|
@ -12,7 +16,7 @@ import {SettingsService} from '../services/settings.service';
|
|||
templateUrl: './sample.component.html',
|
||||
styleUrls: ['./sample.component.scss']
|
||||
})
|
||||
export class SampleComponent implements AfterViewInit {
|
||||
export class SampleComponent implements OnInit, AfterViewInit {
|
||||
settings: AppDefaults;
|
||||
barCodeFormControl: FormControl;
|
||||
initialsFormControl: FormControl;
|
||||
|
@ -24,6 +28,8 @@ export class SampleComponent implements AfterViewInit {
|
|||
private router: Router,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
private settingsService: SettingsService,
|
||||
private cacheService: CacheService,
|
||||
private apiService: ApiService
|
||||
) {
|
||||
this.settings = this.settingsService.getSettings();
|
||||
this.barCodeFormControl = new FormControl('', [
|
||||
|
@ -65,6 +71,29 @@ export class SampleComponent implements AfterViewInit {
|
|||
return !(this.barCodeFormControl.valid && this.initialsFormControl.valid);
|
||||
}
|
||||
|
||||
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.');
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
this.barCodeInput.focus();
|
||||
this.changeDetector.detectChanges();
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
<input
|
||||
#locationIdInput="matInput"
|
||||
matInput
|
||||
type="text"
|
||||
type="number"
|
||||
[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>
|
||||
<mat-error *ngIf="locationIdFormControl.hasError('pattern')">Please enter a number between 1 and 4 digits long.</mat-error>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="label-layout-select" fxFlex="95%">
|
||||
<mat-label>Label layout</mat-label>
|
||||
|
@ -39,7 +39,7 @@
|
|||
{{layout.name}}
|
||||
</div>
|
||||
<div fxFlex="50%">
|
||||
<app-circle-qrcode-single *ngIf="layout.id === 'circle_qrcode_single'" [sample]="fakeSample"></app-circle-qrcode-single>
|
||||
<app-circle-qrcode-single *ngIf="layout.id === 'circle_qrcode_single'" [sample]="sampleForLayout(layout)"></app-circle-qrcode-single>
|
||||
<app-circle-qrcode-double *ngIf="layout.id === 'circle_qrcode_double'" [sample]="fakeSample"></app-circle-qrcode-double>
|
||||
<app-rectangle-code128 *ngIf="layout.id === 'rectangle_code128'" [sample]="fakeSample"></app-rectangle-code128>
|
||||
<app-rectangle-datamatrix *ngIf="layout.id === 'rectangle_datamatrix'" [sample]="fakeSample"></app-rectangle-datamatrix>
|
||||
|
|
|
@ -8,6 +8,7 @@ import {AppDefaults} from '../models/appDefaults.interface';
|
|||
import {LabelLayout} from '../models/labelLayout.interface';
|
||||
import {Sample} from '../models/sample.interface';
|
||||
import {SettingsService} from '../services/settings.service';
|
||||
import createClone from 'rfdc';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
|
@ -70,7 +71,7 @@ export class SettingsComponent implements AfterViewInit {
|
|||
this.settingsService.saveSettings({
|
||||
labelLayout: labelLayouts[this.labelLayoutFormControl.value],
|
||||
numCopies: this.numCopiesFormControl.value,
|
||||
locationId: this.locationIdFormControl.value,
|
||||
locationId: this.locationIdFormControl.value.toString().padStart(4, '0'),
|
||||
});
|
||||
this.router.navigate(['/']);
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ export class SettingsComponent implements AfterViewInit {
|
|||
this.fakeSample = {
|
||||
barcode: '',
|
||||
student_id: '123456789',
|
||||
initials: 'ABCDE',
|
||||
initials: 'ABC9Z',
|
||||
date: new Date(),
|
||||
location: this.settings.locationId,
|
||||
};
|
||||
|
@ -90,7 +91,9 @@ export class SettingsComponent implements AfterViewInit {
|
|||
this.fakeSample.student_id,
|
||||
this.fakeSample.initials,
|
||||
this.fakeSample.date,
|
||||
this.fakeSample.location
|
||||
this.fakeSample.location,
|
||||
'-',
|
||||
'datamatrix',
|
||||
);
|
||||
|
||||
this.fakeSample.barcode = this.fakeBarcodeValue;
|
||||
|
@ -99,4 +102,18 @@ export class SettingsComponent implements AfterViewInit {
|
|||
selectLabelLayout(layout: LabelLayout) {
|
||||
this.labelLayoutFormControl.patchValue(layout);
|
||||
}
|
||||
|
||||
sampleForLayout(layout: LabelLayout) {
|
||||
const sample: Sample = createClone()(this.fakeSample);
|
||||
sample.barcode = createQrCodeValue(
|
||||
sample.student_id,
|
||||
sample.initials,
|
||||
sample.date,
|
||||
sample.location,
|
||||
layout.delimiter,
|
||||
layout.barcodeType,
|
||||
);
|
||||
|
||||
return sample;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,8 @@ export class MockEnvironment implements AppEnvironment {
|
|||
api = 'apiRoot';
|
||||
title = 'Mock Title';
|
||||
googleAnalyticsKey = 'SOME_KEY';
|
||||
|
||||
constructor() {
|
||||
console.log('MockEnvironment constructor');
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue