mirror of
https://github.com/sartography/uva-covid19-testing-frontend.git
synced 2025-02-16 17:47:07 +00:00
Merge pull request #2 from sartography/bug/layout-problems
Bug/layout problems
This commit is contained in:
commit
95fe8e413d
@ -29,6 +29,11 @@
|
|||||||
],
|
],
|
||||||
"styles": [
|
"styles": [
|
||||||
"src/styles.scss"
|
"src/styles.scss"
|
||||||
|
],
|
||||||
|
"allowedCommonJsDependencies": [
|
||||||
|
"bwip-js",
|
||||||
|
"qrcode",
|
||||||
|
"serialize-javascript"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"configurations": {
|
"configurations": {
|
||||||
|
@ -10,9 +10,10 @@ describe('COVID19 Testing Kiosk App', () => {
|
|||||||
http = new HttpClient('http://localhost:5001');
|
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();
|
page.navigateTo();
|
||||||
expect(page.getRoute()).toEqual('/');
|
expect(page.getRoute()).toEqual('/');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to settings screen', () => {
|
it('should navigate to settings screen', () => {
|
||||||
@ -23,15 +24,7 @@ describe('COVID19 Testing Kiosk App', () => {
|
|||||||
page.clickAndExpectRoute('#nav_home', '/');
|
page.clickAndExpectRoute('#nav_home', '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to occupancy count input screen', () => {
|
it('should navigate back to sample input screen', () => {
|
||||||
page.clickAndExpectRoute('#nav_count', '/count');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should navigate back to home screen', () => {
|
|
||||||
page.clickAndExpectRoute('#nav_home', '/');
|
page.clickAndExpectRoute('#nav_home', '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should navigate to sample input screen', () => {
|
|
||||||
page.clickAndExpectRoute('#nav_sample', '/sample');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
5
package-lock.json
generated
5
package-lock.json
generated
@ -2959,6 +2959,11 @@
|
|||||||
"integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
|
"integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"bwip-js": {
|
||||||
|
"version": "2.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-2.0.11.tgz",
|
||||||
|
"integrity": "sha512-8hAwUM+5FrGguKRaFi2QxKF9SxQM4fAfTy5TVAkeBODH6Eq3Q8MMEoBN+ymIBxiGKL+wNHB1Z1pv5f9oWGLUKQ=="
|
||||||
|
},
|
||||||
"bytes": {
|
"bytes": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
"@angular/router": "~11.0.0-next.2",
|
"@angular/router": "~11.0.0-next.2",
|
||||||
"@ngx-formly/core": "^5.8.0",
|
"@ngx-formly/core": "^5.8.0",
|
||||||
"@ngx-formly/material": "^5.8.0",
|
"@ngx-formly/material": "^5.8.0",
|
||||||
|
"bwip-js": "^2.0.11",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"ngx-qrcode-svg": "^2.0.0",
|
"ngx-qrcode-svg": "^2.0.0",
|
||||||
"rfdc": "^1.1.4",
|
"rfdc": "^1.1.4",
|
||||||
|
@ -3,7 +3,7 @@ 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): string => {
|
||||||
const valArray = [
|
const valArray = [
|
||||||
barCode,
|
barCode,
|
||||||
initials,
|
initials.toUpperCase(),
|
||||||
formatDate(dateCreated, 'yyyyMMddHHmm', 'en-us'),
|
formatDate(dateCreated, 'yyyyMMddHHmm', 'en-us'),
|
||||||
locationId,
|
locationId,
|
||||||
];
|
];
|
||||||
|
@ -11,7 +11,7 @@ export const routes: Routes = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
pathMatch: 'full',
|
pathMatch: 'full',
|
||||||
component: HomeComponent
|
component: SampleComponent
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'sample',
|
path: 'sample',
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="mat-typography">
|
<div class="mat-typography">
|
||||||
<app-navbar [testingLocation]="testingLocation"></app-navbar>
|
<app-navbar></app-navbar>
|
||||||
<router-outlet *ngIf="!loading; else loadingMessage"></router-outlet>
|
<router-outlet *ngIf="!loading; else loadingMessage"></router-outlet>
|
||||||
<app-footer></app-footer>
|
<app-footer></app-footer>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,22 +1,15 @@
|
|||||||
import {Component, Inject} from '@angular/core';
|
import {Component, Inject} from '@angular/core';
|
||||||
import {MatIconRegistry} from '@angular/material/icon';
|
import {Title} from '@angular/platform-browser';
|
||||||
import {DomSanitizer, Title} from '@angular/platform-browser';
|
|
||||||
import {Router} from '@angular/router';
|
|
||||||
import {AppEnvironment} from './models/appEnvironment.interface';
|
import {AppEnvironment} from './models/appEnvironment.interface';
|
||||||
import {TestingLocation} from './models/testingLocation.interface';
|
|
||||||
import {GoogleAnalyticsService} from './services/google-analytics.service';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.scss']
|
styleUrls: ['./app.component.scss']
|
||||||
})
|
})
|
||||||
|
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
testingLocation: TestingLocation = {
|
|
||||||
id: '0000',
|
|
||||||
name: 'Click here to set location',
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
@Inject('APP_ENVIRONMENT') private environment: AppEnvironment,
|
||||||
@ -25,6 +18,7 @@ export class AppComponent {
|
|||||||
this.titleService.setTitle(this.environment.title);
|
this.titleService.setTitle(this.environment.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
reload() {
|
reload() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
setTimeout(() => this.loading = false, 300);
|
setTimeout(() => this.loading = false, 300);
|
||||||
|
@ -18,9 +18,14 @@ import {QRCodeSVGModule} from 'ngx-qrcode-svg';
|
|||||||
import {ThisEnvironment} from '../environments/environment.injectable';
|
import {ThisEnvironment} from '../environments/environment.injectable';
|
||||||
import {AppRoutingModule} from './app-routing.module';
|
import {AppRoutingModule} from './app-routing.module';
|
||||||
import {AppComponent} from './app.component';
|
import {AppComponent} from './app.component';
|
||||||
|
import {BarcodeSvgDirective} from './barcode-svg/barcode-svg.directive';
|
||||||
import {CountComponent} from './count/count.component';
|
import {CountComponent} from './count/count.component';
|
||||||
import {FooterComponent} from './footer/footer.component';
|
import {FooterComponent} from './footer/footer.component';
|
||||||
import {HomeComponent} from './home/home.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';
|
||||||
|
import {RectangleDatamatrixComponent} from './label-layout/formats/rectangle-datamatrix/rectangle-datamatrix.component';
|
||||||
import {LabelLayoutComponent} from './label-layout/label-layout.component';
|
import {LabelLayoutComponent} from './label-layout/label-layout.component';
|
||||||
import {LoadingComponent} from './loading/loading.component';
|
import {LoadingComponent} from './loading/loading.component';
|
||||||
import {NavbarComponent} from './navbar/navbar.component';
|
import {NavbarComponent} from './navbar/navbar.component';
|
||||||
@ -50,6 +55,9 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
|
BarcodeSvgDirective,
|
||||||
|
CircleQRcodeDoubleComponent,
|
||||||
|
CircleQRcodeSingleComponent,
|
||||||
CountComponent,
|
CountComponent,
|
||||||
FooterComponent,
|
FooterComponent,
|
||||||
HomeComponent,
|
HomeComponent,
|
||||||
@ -58,6 +66,8 @@ export function getBaseHref(platformLocation: PlatformLocation): string {
|
|||||||
NavbarComponent,
|
NavbarComponent,
|
||||||
PrintComponent,
|
PrintComponent,
|
||||||
PrintLayoutComponent,
|
PrintLayoutComponent,
|
||||||
|
RectangleCode128Component,
|
||||||
|
RectangleDatamatrixComponent,
|
||||||
SampleComponent,
|
SampleComponent,
|
||||||
SettingsComponent,
|
SettingsComponent,
|
||||||
],
|
],
|
||||||
|
8
src/app/barcode-svg/barcode-svg.directive.spec.ts
Normal file
8
src/app/barcode-svg/barcode-svg.directive.spec.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { BarcodeSvgDirective } from './barcode-svg.directive';
|
||||||
|
|
||||||
|
describe('BarcodeSvgDirective', () => {
|
||||||
|
it('should create an instance', () => {
|
||||||
|
const directive = new BarcodeSvgDirective(null);
|
||||||
|
expect(directive).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
40
src/app/barcode-svg/barcode-svg.directive.ts
Normal file
40
src/app/barcode-svg/barcode-svg.directive.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import bwipjs from 'bwip-js';
|
||||||
|
import DrawingSVG from './draw-svg.js';
|
||||||
|
import {Directive, ElementRef, Input, OnInit} from '@angular/core';
|
||||||
|
|
||||||
|
@Directive({
|
||||||
|
selector: '[appBarcodeSvg]'
|
||||||
|
})
|
||||||
|
export class BarcodeSvgDirective implements OnInit {
|
||||||
|
@Input() format: string;
|
||||||
|
@Input() value: string;
|
||||||
|
@Input() x: number;
|
||||||
|
@Input() y: number;
|
||||||
|
@Input() width: number;
|
||||||
|
@Input() height: number;
|
||||||
|
|
||||||
|
constructor(private barcodeContainer: ElementRef) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
if (!!(bwipjs && bwipjs.render && this.format)) {
|
||||||
|
const opts: { [key: string]: any } = {
|
||||||
|
bcid: this.format,
|
||||||
|
text: this.value,
|
||||||
|
scale: 1,
|
||||||
|
width: this.width,
|
||||||
|
height: this.height,
|
||||||
|
// includetext: false,
|
||||||
|
// textalign: 'center',
|
||||||
|
// version: '12x64',
|
||||||
|
// padding: this.settings.labelLayout.marginSize,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.format === 'qrcode') {
|
||||||
|
opts.eclevel = 'H';
|
||||||
|
}
|
||||||
|
this.barcodeContainer.nativeElement.innerHTML = bwipjs.render(opts, DrawingSVG(opts, bwipjs.FontLib));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
270
src/app/barcode-svg/draw-svg.js
Normal file
270
src/app/barcode-svg/draw-svg.js
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// bwip-js/examples/drawing-svg.js
|
||||||
|
//
|
||||||
|
// This is an advanced demonstation of using the drawing interface.
|
||||||
|
//
|
||||||
|
// It converts the drawing primitives into the equivalent SVG. Linear barcodes
|
||||||
|
// are rendered as a series of stroked paths. 2D barcodes are rendered as a
|
||||||
|
// series of filled paths.
|
||||||
|
//
|
||||||
|
// Rotation is handled during drawing. The resulting SVG will contain the
|
||||||
|
// already-rotated barcode without an SVG transform.
|
||||||
|
//
|
||||||
|
// If the requested barcode image contains text, the glyph paths are
|
||||||
|
// extracted from the font file (via the builtin FontLib and stb_truetype.js)
|
||||||
|
// and added as filled SVG paths.
|
||||||
|
//
|
||||||
|
// This code can run in the browser and in nodejs.
|
||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
define([], factory);
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
root.DrawingSVG = factory();
|
||||||
|
}
|
||||||
|
}(typeof self !== 'undefined' ? self : this, function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function DrawingSVG(opts, FontLib) {
|
||||||
|
// Unrolled x,y rotate/translate matrix
|
||||||
|
var tx0 = 0, tx1 = 0, tx2 = 0, tx3 = 0;
|
||||||
|
var ty0 = 0, ty1 = 0, ty2 = 0, ty3 = 0;
|
||||||
|
|
||||||
|
var svg = '';
|
||||||
|
var path;
|
||||||
|
var lines = {};
|
||||||
|
|
||||||
|
// Magic number to approximate an ellipse/circle using 4 cubic beziers.
|
||||||
|
var ELLIPSE_MAGIC = 0.55228475 - 0.00045;
|
||||||
|
|
||||||
|
// Global graphics state
|
||||||
|
var gs_width, gs_height; // image size, in pixels
|
||||||
|
var gs_dx, gs_dy; // x,y translate (padding)
|
||||||
|
|
||||||
|
return {
|
||||||
|
// Make no adjustments
|
||||||
|
scale(sx, sy) {
|
||||||
|
},
|
||||||
|
// Measure text. This and scale() are the only drawing primitives that
|
||||||
|
// are called before init().
|
||||||
|
//
|
||||||
|
// `font` is the font name typically OCR-A or OCR-B.
|
||||||
|
// `fwidth` and `fheight` are the requested font cell size. They will
|
||||||
|
// usually be the same, except when the scaling is not symetric.
|
||||||
|
measure(str, font, fwidth, fheight) {
|
||||||
|
fwidth = fwidth|0;
|
||||||
|
fheight = fheight|0;
|
||||||
|
|
||||||
|
var fontid = FontLib.lookup(font);
|
||||||
|
var width = 0;
|
||||||
|
var ascent = 0;
|
||||||
|
var descent = 0;
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
var ch = str.charCodeAt(i);
|
||||||
|
var glyph = FontLib.getpaths(fontid, ch, fwidth, fheight);
|
||||||
|
if (!glyph) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ascent = Math.max(ascent, glyph.ascent);
|
||||||
|
descent = Math.max(descent, -glyph.descent);
|
||||||
|
width += glyph.advance;
|
||||||
|
}
|
||||||
|
return { width, ascent, descent };
|
||||||
|
},
|
||||||
|
|
||||||
|
// width and height represent the maximum bounding box the graphics will occupy.
|
||||||
|
// The dimensions are for an unrotated rendering. Adjust as necessary.
|
||||||
|
init(width, height) {
|
||||||
|
// Add in the effects of padding. These are always set before the
|
||||||
|
// drawing constructor is called.
|
||||||
|
var padl = opts.paddingleft || 0;
|
||||||
|
var padr = opts.paddingright || 0;
|
||||||
|
var padt = opts.paddingtop || 0;
|
||||||
|
var padb = opts.paddingbottom || 0;
|
||||||
|
var rot = opts.rotate || 'N';
|
||||||
|
|
||||||
|
width += padl + padr;
|
||||||
|
height += padt + padb;
|
||||||
|
|
||||||
|
// Transform indexes are: x, y, w, h
|
||||||
|
switch (rot) {
|
||||||
|
// tx = w-y, ty = x
|
||||||
|
case 'R': tx1 = -1; tx2 = 1; ty0 = 1; break;
|
||||||
|
// tx = w-x, ty = h-y
|
||||||
|
case 'I': tx0 = -1; tx2 = 1; ty1 = -1; ty3 = 1; break;
|
||||||
|
// tx = y, ty = h-x
|
||||||
|
case 'L': tx1 = 1; ty0 = -1; ty3 = 1; break;
|
||||||
|
// tx = x, ty = y
|
||||||
|
default: tx0 = ty1 = 1; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup the graphics state
|
||||||
|
var swap = rot == 'L' || rot == 'R';
|
||||||
|
gs_width = swap ? height : width;
|
||||||
|
gs_height = swap ? width : height;
|
||||||
|
gs_dx = padl;
|
||||||
|
gs_dy = padt;
|
||||||
|
|
||||||
|
svg = '';
|
||||||
|
},
|
||||||
|
// Unconnected stroked lines are used to draw the bars in linear barcodes.
|
||||||
|
// No line cap should be applied. These lines are always orthogonal.
|
||||||
|
line(x0, y0, x1, y1, lw, rgb) {
|
||||||
|
// Try to get non-blurry lines...
|
||||||
|
x0 = x0|0;
|
||||||
|
y0 = y0|0;
|
||||||
|
x1 = x1|0;
|
||||||
|
y1 = y1|0;
|
||||||
|
lw = Math.round(lw);
|
||||||
|
|
||||||
|
// Try to keep the lines "crisp" by using with the SVG line drawing spec to
|
||||||
|
// our advantage.
|
||||||
|
if (lw & 1) {
|
||||||
|
if (x0 === x1) {
|
||||||
|
x0 += 0.5;
|
||||||
|
x1 += 0.5;
|
||||||
|
}
|
||||||
|
if (y0 === y1) {
|
||||||
|
y0 += 0.5;
|
||||||
|
y1 += 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group together all lines of the same width and emit as single paths.
|
||||||
|
// Dramatically reduces resulting text size.
|
||||||
|
var key = '' + lw + '#' + rgb;
|
||||||
|
if (!lines[key]) {
|
||||||
|
lines[key] = '<path stroke="#' + rgb + '" stroke-width="' + lw + '" d="';
|
||||||
|
}
|
||||||
|
lines[key] += 'M' + transform(x0, y0) + 'L' + transform(x1, y1);
|
||||||
|
},
|
||||||
|
// Polygons are used to draw the connected regions in a 2d barcode.
|
||||||
|
// These will always be unstroked, filled, non-intersecting,
|
||||||
|
// orthogonal shapes.
|
||||||
|
// You will see a series of polygon() calls, followed by a fill().
|
||||||
|
polygon(pts) {
|
||||||
|
if (!path) {
|
||||||
|
path = '<path d="';
|
||||||
|
}
|
||||||
|
path += 'M' + transform(pts[0][0], pts[0][1]);
|
||||||
|
for (var i = 1, n = pts.length; i < n; i++) {
|
||||||
|
var p = pts[i];
|
||||||
|
path += 'L' + transform(p[0], p[1]);
|
||||||
|
}
|
||||||
|
path += 'Z';
|
||||||
|
},
|
||||||
|
// An unstroked, filled hexagon used by maxicode. You can choose to fill
|
||||||
|
// each individually, or wait for the final fill().
|
||||||
|
//
|
||||||
|
// The hexagon is drawn from the top, counter-clockwise.
|
||||||
|
hexagon(pts, rgb) {
|
||||||
|
this.polygon(pts); // A hexagon is just a polygon...
|
||||||
|
},
|
||||||
|
// An unstroked, filled ellipse. Used by dotcode and maxicode at present.
|
||||||
|
// maxicode issues pairs of ellipse calls (one cw, one ccw) followed by a fill()
|
||||||
|
// to create the bullseye rings. dotcode issues all of its ellipses then a
|
||||||
|
// fill().
|
||||||
|
ellipse(x, y, rx, ry, ccw) {
|
||||||
|
if (!path) {
|
||||||
|
path = '<path d="';
|
||||||
|
}
|
||||||
|
var dx = rx * ELLIPSE_MAGIC;
|
||||||
|
var dy = ry * ELLIPSE_MAGIC;
|
||||||
|
|
||||||
|
// Since we fill with even-odd, don't worry about cw/ccw
|
||||||
|
path += 'M' + transform(x - rx, y) +
|
||||||
|
'C' + transform(x - rx, y - dy) + ' ' +
|
||||||
|
transform(x - dx, y - ry) + ' ' +
|
||||||
|
transform(x, y - ry) +
|
||||||
|
'C' + transform(x + dx, y - ry) + ' ' +
|
||||||
|
transform(x + rx, y - dy) + ' ' +
|
||||||
|
transform(x + rx, y) +
|
||||||
|
'C' + transform(x + rx, y + dy) + ' ' +
|
||||||
|
transform(x + dx, y + ry) + ' ' +
|
||||||
|
transform(x, y + ry) +
|
||||||
|
'C' + transform(x - dx, y + ry) + ' ' +
|
||||||
|
transform(x - rx, y + dy) + ' ' +
|
||||||
|
transform(x - rx, y) +
|
||||||
|
'Z';
|
||||||
|
},
|
||||||
|
// PostScript's default fill rule is even-odd.
|
||||||
|
fill(rgb) {
|
||||||
|
if (path) {
|
||||||
|
svg += path + '" fill="#' + rgb + '" fill-rule="evenodd" />\n';
|
||||||
|
path = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Draw text with optional inter-character spacing. `y` is the baseline.
|
||||||
|
// font is an object with properties { name, width, height, dx }
|
||||||
|
// width and height are the font cell size.
|
||||||
|
// dx is extra space requested between characters (usually zero).
|
||||||
|
text(x, y, str, rgb, font) {
|
||||||
|
var fontid = FontLib.lookup(font.name);
|
||||||
|
var fwidth = font.width|0;
|
||||||
|
var fheight = font.height|0;
|
||||||
|
var dx = font.dx|0;
|
||||||
|
var path = '';
|
||||||
|
for (var k = 0; k < str.length; k++) {
|
||||||
|
var ch = str.charCodeAt(k);
|
||||||
|
var glyph = FontLib.getpaths(fontid, ch, fwidth, fheight);
|
||||||
|
if (!glyph) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (glyph.length) {
|
||||||
|
// A glyph is composed of sequence of curve and line segments.
|
||||||
|
// M is move-to
|
||||||
|
// L is line-to
|
||||||
|
// Q is quadratic bezier curve-to
|
||||||
|
// C is cubic bezier curve-to
|
||||||
|
for (var i = 0, l = glyph.length; i < l; i++) {
|
||||||
|
let seg = glyph[i];
|
||||||
|
if (seg.type == 'M' || seg.type == 'L') {
|
||||||
|
path += seg.type + transform(seg.x + x, y - seg.y);
|
||||||
|
} else if (seg.type == 'Q') {
|
||||||
|
path += seg.type + transform(seg.cx + x, y - seg.cy) + ' ' +
|
||||||
|
transform(seg.x + x, y - seg.y);
|
||||||
|
} else if (seg.type == 'C') {
|
||||||
|
path += seg.type + transform(seg.cx1 + x, y - seg.cy1) + ' ' +
|
||||||
|
transform(seg.cx2 + x, y - seg.cy2) + ' ' +
|
||||||
|
transform(seg.x + x, y - seg.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Close the shape
|
||||||
|
path += 'Z';
|
||||||
|
}
|
||||||
|
x += glyph.advance + dx;
|
||||||
|
}
|
||||||
|
if (path) {
|
||||||
|
svg += '<path d="' + path + '" fill="#' + rgb + '" />\n';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Called after all drawing is complete. The return value from this method
|
||||||
|
// is the return value from `bwipjs.render()`.
|
||||||
|
end() {
|
||||||
|
var linesvg = '';
|
||||||
|
for (var key in lines) {
|
||||||
|
linesvg += lines[key] + '" />\n';
|
||||||
|
}
|
||||||
|
var bg = opts.backgroundcolor;
|
||||||
|
return '<svg version="1.1" width="' + gs_width + '" height="' + gs_height +
|
||||||
|
'" xmlns="http://www.w3.org/2000/svg">\n' +
|
||||||
|
(/^[0-9A-Fa-f]{6}$/.test(''+bg)
|
||||||
|
? '<rect width="100%" height="100%" fill="#' + bg + '" />\n'
|
||||||
|
: '') +
|
||||||
|
linesvg + svg + '</svg>\n';
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// translate/rotate and return as an SVG coordinate pair
|
||||||
|
function transform(x, y) {
|
||||||
|
x += gs_dx;
|
||||||
|
y += gs_dy;
|
||||||
|
var tx = tx0 * x + tx1 * y + tx2 * (gs_width-1) + tx3 * (gs_height-1);
|
||||||
|
var ty = ty0 * x + ty1 * y + ty2 * (gs_width-1) + ty3 * (gs_height-1);
|
||||||
|
return '' + ((tx|0) === tx ? tx : tx.toFixed(2)) + ' ' +
|
||||||
|
((ty|0) === ty ? ty : ty.toFixed(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return DrawingSVG;
|
||||||
|
}));
|
@ -2,34 +2,52 @@ import {AppDefaultsOptions} from '../models/appDefaults.interface';
|
|||||||
import {LabelLayout} from '../models/labelLayout.interface';
|
import {LabelLayout} from '../models/labelLayout.interface';
|
||||||
|
|
||||||
export const labelLayouts = {
|
export const labelLayouts = {
|
||||||
round_32mm_1up: new LabelLayout({
|
circle_qrcode_single: new LabelLayout({
|
||||||
name: '32mm Round Label - 1up',
|
name: '32mm Round Label - QR Code (1up)',
|
||||||
type: 'round_32mm_1up',
|
barcodeType: 'qrcode',
|
||||||
numCols: 1,
|
id: 'circle_qrcode_single',
|
||||||
columnGap: 0,
|
pageWidth: 32,
|
||||||
|
pageHeight: 32,
|
||||||
}),
|
}),
|
||||||
round_32mm_2up: new LabelLayout({
|
circle_qrcode_double: new LabelLayout({
|
||||||
name: '32mm Round Label - 2up',
|
name: '32mm Round Label - QR Code (2up)',
|
||||||
type: 'round_32mm_2up',
|
barcodeType: 'qrcode',
|
||||||
numCols: 2,
|
id: 'circle_qrcode_double',
|
||||||
columnGap: 3.4,
|
pageWidth: 64,
|
||||||
|
pageHeight: 32,
|
||||||
|
}),
|
||||||
|
rectangle_code128: new LabelLayout({
|
||||||
|
name: '2in x 1.25in Rectangular Label - CODE128',
|
||||||
|
barcodeType: 'code128',
|
||||||
|
id: 'rectangle_code128',
|
||||||
|
pageWidth: 54,
|
||||||
|
pageHeight: 34,
|
||||||
|
}),
|
||||||
|
rectangle_datamatrix: new LabelLayout({
|
||||||
|
name: '2in x 1.25in Rectangular Label - DataMatrix',
|
||||||
|
barcodeType: 'datamatrix',
|
||||||
|
id: 'rectangle_datamatrix',
|
||||||
|
pageWidth: 54,
|
||||||
|
pageHeight: 34,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultOptions: AppDefaultsOptions = {
|
export const defaultOptions: AppDefaultsOptions = {
|
||||||
barCodeNumLength: 9, // Number of digits in Bar Code.
|
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.
|
barCodeRegExp: /^[\d]{14}$|^[\d]{9}$/, // Pattern for Bar Code data.
|
||||||
// Manually-entered ID numbers will be exactly 9 digits long.
|
// Scanned barcodes will be either 9 or 14 digits long.
|
||||||
countsCollection: 'counts', // Name of collection for Line Counts in Firebase.
|
// Manually-entered ID numbers will be exactly 9 digits long.
|
||||||
dateDisplayFormat: 'MM/dd/yyyy, hh:mm aa', // Format for dates when displayed to user.
|
countsCollection: 'counts', // 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.
|
||||||
|
dateEncodedFormat: 'yyyyMMddHHmm', // Format for dates when encoded in IDs for database records.
|
||||||
initialsLength: 5,
|
initialsLength: 5,
|
||||||
initialsRegExp: /^[a-zA-Z]{2,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.
|
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.
|
lineCountRegExp: /^[\d]{4}-[\d]{12}$/, // ID format for Line Count records.
|
||||||
locationId: '0000', // Default location ID. Can be overridden by user setting.
|
locationId: '0000', // Default location ID. Can be overridden by user setting.
|
||||||
locationIdRegExp: /^[\d]{4}$/, // ID format for Line Count records.
|
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.
|
numCopies: 1, // Default number of copies of labels to print.
|
||||||
qrCodeRegExp: /^[\d]{9}-[a-zA-Z]+-[\d]{12}-[\d]{4}$/, // ID format for QR Code records.
|
// Can be overridden by user setting.
|
||||||
samplesCollection: 'samples', // Name of collection for Line Counts in Firebase.
|
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.
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import {mockSample} from '../../../testing/sample.mock';
|
||||||
|
|
||||||
|
import { CircleQRcodeDoubleComponent } from './circle-qrcode-double.component';
|
||||||
|
|
||||||
|
describe('CircleQRcodeDoubleComponent', () => {
|
||||||
|
let component: CircleQRcodeDoubleComponent;
|
||||||
|
let fixture: ComponentFixture<CircleQRcodeDoubleComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ CircleQRcodeDoubleComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CircleQRcodeDoubleComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.sample = mockSample;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,52 @@
|
|||||||
|
<svg
|
||||||
|
class="label-layout-format"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="64mm"
|
||||||
|
height="32mm"
|
||||||
|
viewBox="0 0 64 32"
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<g id="circle_qrcode_single">
|
||||||
|
<circle
|
||||||
|
class="label-layout-border"
|
||||||
|
cx="16"
|
||||||
|
cy="16"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
stroke="#CCCCCC"
|
||||||
|
stroke-width="0.2"
|
||||||
|
r="14.3"/>
|
||||||
|
<g transform="translate(7, 6.8)">
|
||||||
|
<g
|
||||||
|
appBarcodeSvg
|
||||||
|
[format]="'qrcode'"
|
||||||
|
[value]="sample.barcode"
|
||||||
|
[width]="18"
|
||||||
|
[height]="18"
|
||||||
|
transform="scale(0.35)"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(4.2, 14)">
|
||||||
|
<text x="0" y="0">T</text>
|
||||||
|
<text x="0" y="2">{{sample.date | date: 'HH'}}</text>
|
||||||
|
<text x="0" y="4">{{sample.date | date: 'mm'}}</text>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(27.4, 14)">
|
||||||
|
<text x="0" y="0">L</text>
|
||||||
|
<text x="0" y="2">{{sample.location.slice(0,2)}}</text>
|
||||||
|
<text x="0" y="4">{{sample.location.slice(2,4)}}</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<use xlink:href="#circle_qrcode_single" x="0" y="0"></use>
|
||||||
|
<use xlink:href="#circle_qrcode_single" x="32" y="0"></use>
|
||||||
|
<g>
|
||||||
|
<text x="25%" y="6">#{{sample.student_id}}</text>
|
||||||
|
<text x="25%" y="27.6">{{sample.date | date:'yyMMdd'}}{{sample.initials}}</text>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<text x="75%" y="6">#{{sample.student_id}}</text>
|
||||||
|
<text x="75%" y="27.6">{{sample.date | date:'yyMMdd'}}{{sample.initials}}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,23 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {AppDefaults} from '../../../models/appDefaults.interface';
|
||||||
|
import {Sample} from '../../../models/sample.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-circle-qrcode-double',
|
||||||
|
templateUrl: './circle-qrcode-double.component.svg',
|
||||||
|
styleUrls: ['./circle-qrcode-double.component.scss']
|
||||||
|
})
|
||||||
|
export class CircleQRcodeDoubleComponent implements OnInit {
|
||||||
|
@Input() sample: Sample;
|
||||||
|
@Input() settings: AppDefaults;
|
||||||
|
@Input() x: number;
|
||||||
|
@Input() y: number;
|
||||||
|
@Input() width: number;
|
||||||
|
@Input() height: number;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import {mockSample} from '../../../testing/sample.mock';
|
||||||
|
|
||||||
|
import { CircleQRcodeSingleComponent } from './circle-qrcode-single.component';
|
||||||
|
|
||||||
|
describe('CircleQRcodeSingleComponent', () => {
|
||||||
|
let component: CircleQRcodeSingleComponent;
|
||||||
|
let fixture: ComponentFixture<CircleQRcodeSingleComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ CircleQRcodeSingleComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(CircleQRcodeSingleComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.sample = mockSample;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,40 @@
|
|||||||
|
<svg
|
||||||
|
class="label-layout-format"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="32mm"
|
||||||
|
height="32mm"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
class="label-layout-border"
|
||||||
|
cx="16"
|
||||||
|
cy="16"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
stroke="#CCCCCC"
|
||||||
|
stroke-width="0.2"
|
||||||
|
r="14.3"/>
|
||||||
|
<g transform="translate(7, 6.8)">
|
||||||
|
<g
|
||||||
|
appBarcodeSvg
|
||||||
|
[format]="'qrcode'"
|
||||||
|
[value]="sample.barcode"
|
||||||
|
[width]="18"
|
||||||
|
[height]="18"
|
||||||
|
transform="scale(0.35)"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
<text x="50%" y="6">#{{sample.student_id}}</text>
|
||||||
|
<text x="50%" y="27.6">{{sample.date | date:'yyMMdd'}}{{sample.initials}}</text>
|
||||||
|
<g transform="translate(4.2, 14)">
|
||||||
|
<text x="0" y="0">T</text>
|
||||||
|
<text x="0" y="2">{{sample.date | date: 'HH'}}</text>
|
||||||
|
<text x="0" y="4">{{sample.date | date: 'mm'}}</text>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(27.4, 14)">
|
||||||
|
<text x="0" y="0">L</text>
|
||||||
|
<text x="0" y="2">{{sample.location.slice(0,2)}}</text>
|
||||||
|
<text x="0" y="4">{{sample.location.slice(2,4)}}</text>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,27 @@
|
|||||||
|
import {ChangeDetectorRef, Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {AppDefaults} from '../../../models/appDefaults.interface';
|
||||||
|
import {Sample} from '../../../models/sample.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-circle-qrcode-single',
|
||||||
|
templateUrl: './circle-qrcode-single.component.svg',
|
||||||
|
styleUrls: ['./circle-qrcode-single.component.scss']
|
||||||
|
})
|
||||||
|
export class CircleQRcodeSingleComponent implements OnInit {
|
||||||
|
@Input() sample: Sample;
|
||||||
|
@Input() settings: AppDefaults;
|
||||||
|
@Input() x: number;
|
||||||
|
@Input() y: number;
|
||||||
|
@Input() width: number;
|
||||||
|
@Input() height: number;
|
||||||
|
|
||||||
|
constructor(private changeDetector: ChangeDetectorRef) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
if (this.sample) {
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import {mockSample} from '../../../testing/sample.mock';
|
||||||
|
|
||||||
|
import { RectangleCode128Component } from './rectangle-code128.component';
|
||||||
|
|
||||||
|
describe('RectangleCode128Component', () => {
|
||||||
|
let component: RectangleCode128Component;
|
||||||
|
let fixture: ComponentFixture<RectangleCode128Component>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ RectangleCode128Component ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RectangleCode128Component);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.sample = mockSample;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,46 @@
|
|||||||
|
<svg
|
||||||
|
class="label-layout-format"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="54mm"
|
||||||
|
height="34mm"
|
||||||
|
viewBox="0 0 54 34"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<text x="1" y="0">UP</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(1.5, 1)">
|
||||||
|
<rect
|
||||||
|
class="label-layout-border"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
stroke="#CCCCCC"
|
||||||
|
stroke-width="0.2"
|
||||||
|
width="51"
|
||||||
|
height="32"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
rx="2"
|
||||||
|
/>
|
||||||
|
<use xlink:href="#up_arrow" x="2" y="2" />
|
||||||
|
<g transform="translate(2, 11)">
|
||||||
|
<g
|
||||||
|
appBarcodeSvg
|
||||||
|
[format]="'code128'"
|
||||||
|
[height]="10"
|
||||||
|
[value]="sample.barcode"
|
||||||
|
[width]="47"
|
||||||
|
transform="scale(0.345)"/>
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#up_arrow" x="2" y="27.5" />
|
||||||
|
</g>
|
||||||
|
<text x="50%" y="75%">{{sample.barcode}}</text>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,25 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {AppDefaults} from '../../../models/appDefaults.interface';
|
||||||
|
import {Sample} from '../../../models/sample.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-rectangle-code128',
|
||||||
|
templateUrl: './rectangle-code128.component.svg',
|
||||||
|
styleUrls: ['./rectangle-code128.component.scss']
|
||||||
|
})
|
||||||
|
export class RectangleCode128Component implements OnInit {
|
||||||
|
@Input() sample: Sample;
|
||||||
|
@Input() settings: AppDefaults;
|
||||||
|
@Input() x: number;
|
||||||
|
@Input() y: number;
|
||||||
|
@Input() width: number;
|
||||||
|
@Input() height: number;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
// Replace "#barcode" element with svg of barcode
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import {mockSample} from '../../../testing/sample.mock';
|
||||||
|
|
||||||
|
import { RectangleDatamatrixComponent } from './rectangle-datamatrix.component';
|
||||||
|
|
||||||
|
describe('RectangleDatamatrixComponent', () => {
|
||||||
|
let component: RectangleDatamatrixComponent;
|
||||||
|
let fixture: ComponentFixture<RectangleDatamatrixComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ RectangleDatamatrixComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(RectangleDatamatrixComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
component.sample = mockSample;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,56 @@
|
|||||||
|
<svg
|
||||||
|
class="label-layout-format"
|
||||||
|
version="1.1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
width="54mm"
|
||||||
|
height="34mm"
|
||||||
|
viewBox="0 0 54 34"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
<text x="1" y="0">UP</text>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<g transform="translate(1.5, 1)">
|
||||||
|
<rect
|
||||||
|
class="label-layout-border"
|
||||||
|
fill="#FFFFFF"
|
||||||
|
stroke="#CCCCCC"
|
||||||
|
stroke-width="0.2"
|
||||||
|
width="51"
|
||||||
|
height="32"
|
||||||
|
x="0"
|
||||||
|
y="0"
|
||||||
|
rx="2"
|
||||||
|
/>
|
||||||
|
<use xlink:href="#up_arrow" x="2" y="2" />
|
||||||
|
<g
|
||||||
|
appBarcodeSvg
|
||||||
|
format="datamatrix"
|
||||||
|
[value]="sample.barcode"
|
||||||
|
[width]="3.5"
|
||||||
|
[height]="3.5"
|
||||||
|
transform="translate(3.5, 10)"
|
||||||
|
/>
|
||||||
|
<g transform="translate(18, 14)">
|
||||||
|
<text x="0" y="0" style="text-anchor: start; text-align: left;">
|
||||||
|
#{{sample.student_id}}
|
||||||
|
</text>
|
||||||
|
<text x="0" y="3" style="text-anchor: start; text-align: left;">
|
||||||
|
{{sample.date | date:'yyyy-MM-dd HH:mm'}}
|
||||||
|
</text>
|
||||||
|
<text x="0" y="6" style="text-anchor: start; text-align: left;">
|
||||||
|
{{sample.initials}} {{sample.location}}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<use xlink:href="#up_arrow" x="2" y="27.5" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,23 @@
|
|||||||
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
|
import {AppDefaults} from '../../../models/appDefaults.interface';
|
||||||
|
import {Sample} from '../../../models/sample.interface';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-rectangle-datamatrix',
|
||||||
|
templateUrl: './rectangle-datamatrix.component.svg',
|
||||||
|
styleUrls: ['./rectangle-datamatrix.component.scss']
|
||||||
|
})
|
||||||
|
export class RectangleDatamatrixComponent implements OnInit {
|
||||||
|
@Input() sample: Sample;
|
||||||
|
@Input() settings: AppDefaults;
|
||||||
|
@Input() x: number;
|
||||||
|
@Input() y: number;
|
||||||
|
@Input() width: number;
|
||||||
|
@Input() height: number;
|
||||||
|
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,18 +1,4 @@
|
|||||||
<div class="label-layout">
|
<app-circle-qrcode-single *ngIf="isLayout('circle_qrcode_single')" [sample]="sample"></app-circle-qrcode-single>
|
||||||
<div class="date">{{dateCreated | date:'yyMMdd'}}{{initials}}</div>
|
<app-circle-qrcode-double *ngIf="isLayout('circle_qrcode_double')" [sample]="sample"></app-circle-qrcode-double>
|
||||||
<div class="time">
|
<app-rectangle-code128 *ngIf="isLayout('rectangle_code128')" [sample]="sample"></app-rectangle-code128>
|
||||||
T<br />
|
<app-rectangle-datamatrix *ngIf="isLayout('rectangle_datamatrix')" [sample]="sample"></app-rectangle-datamatrix>
|
||||||
{{dateCreated | date:'HH'}}<br />
|
|
||||||
{{dateCreated | date:'mm'}}
|
|
||||||
</div>
|
|
||||||
<qrcode-svg
|
|
||||||
[value]="qrCodeValue"
|
|
||||||
errorCorrectionLevel="H"
|
|
||||||
></qrcode-svg>
|
|
||||||
<div class="location">
|
|
||||||
L<br />
|
|
||||||
{{settings.locationId.slice(0, 2)}}<br />
|
|
||||||
{{settings.locationId.slice(2, 4)}}
|
|
||||||
</div>
|
|
||||||
<div class="barcode">#{{barCode}}</div>
|
|
||||||
</div>
|
|
||||||
|
@ -1,51 +1,13 @@
|
|||||||
.date,
|
::ng-deep app-circle-qrcode-single,
|
||||||
.time,
|
::ng-deep app-circle-qrcode-double,
|
||||||
.location,
|
::ng-deep app-rectangle-code128,
|
||||||
.barcode {
|
::ng-deep app-rectangle-datamatrix {
|
||||||
position: absolute;
|
background-color: transparent;
|
||||||
font-family: monospace;
|
margin: 0 !important;
|
||||||
font-weight: bolder;
|
padding: 0 !important;
|
||||||
text-align: center;
|
overflow: hidden !important;
|
||||||
font-size: 6pt;
|
line-height: 0;
|
||||||
}
|
width: inherit;
|
||||||
|
height: inherit;
|
||||||
.label-layout {
|
display: block;
|
||||||
position: relative;
|
|
||||||
width: 24.6mm;
|
|
||||||
height: 24.6mm;
|
|
||||||
border: 2mm solid white;
|
|
||||||
border-radius: 100%;
|
|
||||||
background-color: white;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.date {
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.barcode {
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
top: calc(50% - 3mm);
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.location {
|
|
||||||
top: calc(50% - 3mm);
|
|
||||||
right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
qrcode-svg {
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
top: 50%;
|
|
||||||
margin-left: -11mm;
|
|
||||||
margin-top: -11mm;
|
|
||||||
width: 22mm;
|
|
||||||
height: 22mm;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import {Component, Input, OnInit} from '@angular/core';
|
import {Component, Input, OnInit} from '@angular/core';
|
||||||
import {createQrCodeValue} from '../_util/qrCode';
|
import {createQrCodeValue} from '../_util/qrCode';
|
||||||
import {AppDefaults} from '../models/appDefaults.interface';
|
import {AppDefaults} from '../models/appDefaults.interface';
|
||||||
|
import {CssStyle} from '../models/cssStyle.interface';
|
||||||
|
import {Sample} from '../models/sample.interface';
|
||||||
import {SettingsService} from '../services/settings.service';
|
import {SettingsService} from '../services/settings.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -8,26 +10,36 @@ import {SettingsService} from '../services/settings.service';
|
|||||||
templateUrl: './label-layout.component.html',
|
templateUrl: './label-layout.component.html',
|
||||||
styleUrls: ['./label-layout.component.scss']
|
styleUrls: ['./label-layout.component.scss']
|
||||||
})
|
})
|
||||||
export class LabelLayoutComponent implements OnInit {
|
export class LabelLayoutComponent {
|
||||||
@Input() dateCreated: Date;
|
@Input() dateCreated: Date;
|
||||||
@Input() barCode: string;
|
@Input() barCode: string;
|
||||||
@Input() initials: string;
|
@Input() initials: string;
|
||||||
settings: AppDefaults;
|
settings: AppDefaults;
|
||||||
|
sample: Sample;
|
||||||
|
barcodeValue: string;
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService) {
|
constructor(private settingsService: SettingsService) {
|
||||||
this.settings = this.settingsService.getSettings();
|
this.settings = this.settingsService.getSettings();
|
||||||
}
|
|
||||||
|
|
||||||
get qrCodeValue(): string {
|
this.sample = {
|
||||||
return createQrCodeValue(
|
barcode: '',
|
||||||
this.barCode,
|
student_id: '123456789',
|
||||||
this.initials,
|
initials: 'ABCDE',
|
||||||
this.dateCreated,
|
date: new Date(),
|
||||||
this.settings.locationId
|
location: this.settings.locationId,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.barcodeValue = createQrCodeValue(
|
||||||
|
this.sample.student_id,
|
||||||
|
this.sample.initials,
|
||||||
|
this.sample.date,
|
||||||
|
this.sample.location
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.sample.barcode = this.barcodeValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
isLayout(layoutId: string) {
|
||||||
|
return this.settings.labelLayout.id === layoutId;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
1
src/app/models/cssStyle.interface.ts
Normal file
1
src/app/models/cssStyle.interface.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export interface CssStyle { [klass: string]: any; }
|
@ -1,78 +1,42 @@
|
|||||||
export interface LayoutOptions {
|
export interface LayoutOptions {
|
||||||
type?: string;
|
barcodeType?: string;
|
||||||
|
id?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
units?: string;
|
units?: string;
|
||||||
pointsPerUnit?: number;
|
pageHeight?: number;
|
||||||
labelSize?: number;
|
pageWidth?: number;
|
||||||
marginSize?: number;
|
|
||||||
numCols?: number;
|
numCols?: number;
|
||||||
columnGap?: number;
|
|
||||||
sideTextWidth?: number;
|
|
||||||
sideTextTop?: number;
|
|
||||||
sideTextMargin?: number;
|
|
||||||
topTextMargin?: number;
|
|
||||||
bottomTextMargin?: number;
|
|
||||||
fontSizePt?: number;
|
|
||||||
numCopies?: number;
|
numCopies?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LabelLayout {
|
export class LabelLayout {
|
||||||
type = 'round_32mm_1up';
|
name = '32mm Round Label - QR Code (1up)';
|
||||||
name = '32mm Round Label - 1up';
|
barcodeType = 'qrcode';
|
||||||
|
id = 'circle_qrcode_double';
|
||||||
units = 'mm';
|
units = 'mm';
|
||||||
pointsPerUnit = 0.3528;
|
pageHeight = 32;
|
||||||
labelSize = 28.6;
|
pageWidth = 32;
|
||||||
marginSize = 1.7;
|
|
||||||
numCols = 1;
|
numCols = 1;
|
||||||
columnGap = 4;
|
|
||||||
sideTextWidth = 4;
|
|
||||||
sideTextTop = 11;
|
|
||||||
sideTextMargin = 1.5;
|
|
||||||
topTextMargin = 3;
|
|
||||||
bottomTextMargin = 2.5;
|
|
||||||
fontSizePt = 6;
|
|
||||||
numCopies = 1;
|
numCopies = 1;
|
||||||
|
|
||||||
constructor(private options: LayoutOptions) {
|
constructor(private options: LayoutOptions) {
|
||||||
const keys = Object.keys(options);
|
if (options) {
|
||||||
keys.forEach(k => {
|
const keys = Object.keys(options);
|
||||||
this[k] = options[k];
|
keys.forEach(k => {
|
||||||
});
|
this[k] = options[k];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('options is empty:', options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get dimensions() {
|
get dimensions() {
|
||||||
return {
|
return {
|
||||||
bottomTextMargin: this._toUnits(this.bottomTextMargin),
|
|
||||||
columnGap: this._toUnits(this.columnGap),
|
|
||||||
fontSize: this._toUnits(this.fontSize),
|
|
||||||
labelSize: this._toUnits(this.labelSize),
|
|
||||||
labelSizeWithMargins: this._toUnits(this.labelSizeWithMargins),
|
|
||||||
marginWidth: this._toUnits(this.marginSize),
|
|
||||||
pageHeight: this._toUnits(this.pageHeight),
|
|
||||||
pageWidth: this._toUnits(this.pageWidth),
|
pageWidth: this._toUnits(this.pageWidth),
|
||||||
sideTextMargin: this._toUnits(this.sideTextMargin),
|
pageHeight: this._toUnits(this.pageHeight),
|
||||||
sideTextTop: this._toUnits(this.sideTextTop),
|
|
||||||
sideTextWidth: this._toUnits(this.sideTextWidth),
|
|
||||||
topTextMargin: this._toUnits(this.topTextMargin),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get labelSizeWithMargins(): number {
|
|
||||||
return (this.labelSize + (this.marginSize * 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
get pageWidth(): number {
|
|
||||||
return (this.labelSizeWithMargins * this.numCols);
|
|
||||||
}
|
|
||||||
|
|
||||||
get pageHeight(): number {
|
|
||||||
return (this.labelSizeWithMargins * this.numCopies);
|
|
||||||
}
|
|
||||||
|
|
||||||
get fontSize(): number {
|
|
||||||
return this.fontSizePt * this.pointsPerUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _toUnits(num: number) {
|
private _toUnits(num: number) {
|
||||||
return `${num}${this.units}`;
|
return `${num}${this.units}`;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export interface Sample {
|
export interface Sample {
|
||||||
barcode: string;
|
barcode: string;
|
||||||
student_id: string;
|
student_id: string;
|
||||||
|
initials?: string;
|
||||||
date: Date;
|
date: Date;
|
||||||
location: string;
|
location: string;
|
||||||
}
|
}
|
||||||
|
@ -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 {SettingsService} from '../services/settings.service';
|
||||||
import {TestingLocation} from '../models/testingLocation.interface';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-navbar',
|
selector: 'app-navbar',
|
||||||
@ -8,16 +8,20 @@ import {TestingLocation} from '../models/testingLocation.interface';
|
|||||||
styleUrls: ['./navbar.component.scss']
|
styleUrls: ['./navbar.component.scss']
|
||||||
})
|
})
|
||||||
export class NavbarComponent implements OnInit {
|
export class NavbarComponent implements OnInit {
|
||||||
@Input() testingLocation: TestingLocation;
|
settings: AppDefaults;
|
||||||
|
locationId: string;
|
||||||
|
|
||||||
constructor(private settingsService: SettingsService) {
|
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 {
|
ngOnInit(): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
get locationId(): string {
|
|
||||||
const settings = this.settingsService.getSettings();
|
|
||||||
return settings.locationId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
class="print-layout"
|
class="print-layout"
|
||||||
[style]="{width: pageWidth, height: pageHeight}"
|
[style]="{width: pageWidth, height: pageHeight}"
|
||||||
[gdRows]="pagesToGridFractions"
|
[gdRows]="pagesToGridFractions"
|
||||||
[gdGap]="marginWidth"
|
|
||||||
gdAlignColumns="center"
|
gdAlignColumns="center"
|
||||||
gdAlignRows="center"
|
gdAlignRows="center"
|
||||||
>
|
>
|
||||||
@ -11,12 +10,11 @@
|
|||||||
[gdColumns]="colsToGridFractions"
|
[gdColumns]="colsToGridFractions"
|
||||||
gdAlignColumns="center"
|
gdAlignColumns="center"
|
||||||
gdAlignRows="center"
|
gdAlignRows="center"
|
||||||
[gdGap]="columnGap"
|
|
||||||
>
|
>
|
||||||
<app-label-layout
|
<app-label-layout
|
||||||
*ngFor="let col of columns"
|
*ngFor="let col of columns"
|
||||||
[barCode]="barCode"
|
[barCode]="barCode"
|
||||||
[initials]="initials"
|
[initials]="initials.toUpperCase()"
|
||||||
[dateCreated]="dateCreated"
|
[dateCreated]="dateCreated"
|
||||||
></app-label-layout>
|
></app-label-layout>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,7 @@ describe('PrintLayoutComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(PrintLayoutComponent);
|
fixture = TestBed.createComponent(PrintLayoutComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
component.initials = 'abcde';
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -37,14 +37,6 @@ export class PrintLayoutComponent implements AfterViewInit {
|
|||||||
return this.layout.dimensions.pageWidth;
|
return this.layout.dimensions.pageWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
get marginWidth(): string {
|
|
||||||
return this.layout.dimensions.marginWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
get columnGap(): string {
|
|
||||||
return this.layout.dimensions.columnGap;
|
|
||||||
}
|
|
||||||
|
|
||||||
get pages() {
|
get pages() {
|
||||||
return Array(this.layout.numCopies).fill('');
|
return Array(this.layout.numCopies).fill('');
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
>
|
>
|
||||||
<app-print-layout
|
<app-print-layout
|
||||||
[barCode]="barCode"
|
[barCode]="barCode"
|
||||||
[initials]="initials"
|
[initials]="initials.toUpperCase()"
|
||||||
[dateCreated]="dateCreated"
|
[dateCreated]="dateCreated"
|
||||||
></app-print-layout>
|
></app-print-layout>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
@ -26,7 +26,7 @@
|
|||||||
#saveAndPrintButton="matButton"
|
#saveAndPrintButton="matButton"
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
class="btn-lg"
|
class="btn-lg"
|
||||||
[color]="isSaved ? '' : 'accent'"
|
color="accent"
|
||||||
(click)="saveAndPrint()"
|
(click)="saveAndPrint()"
|
||||||
autofocus
|
autofocus
|
||||||
fxFlex="33%"
|
fxFlex="33%"
|
||||||
@ -37,8 +37,7 @@
|
|||||||
class="btn-lg"
|
class="btn-lg"
|
||||||
routerLink="/"
|
routerLink="/"
|
||||||
fxFlex="33%"
|
fxFlex="33%"
|
||||||
[color]="isSaved ? 'accent' : ''"
|
><mat-icon>cancel</mat-icon> Cancel</button>
|
||||||
><mat-icon>done</mat-icon> Done</button>
|
|
||||||
</mat-card-actions>
|
</mat-card-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
</div>
|
</div>
|
||||||
@ -46,7 +45,7 @@
|
|||||||
<div id="media-print">
|
<div id="media-print">
|
||||||
<app-print-layout
|
<app-print-layout
|
||||||
[barCode]="barCode"
|
[barCode]="barCode"
|
||||||
[initials]="initials"
|
[initials]="initials.toUpperCase()"
|
||||||
[dateCreated]="dateCreated"
|
[dateCreated]="dateCreated"
|
||||||
></app-print-layout>
|
></app-print-layout>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,11 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#media-print { display: block !important; }
|
#media-print {
|
||||||
|
display: block !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
#media-screen { display: none !important; }
|
#media-screen { display: none !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import {AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
|
import {AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
|
||||||
import {MatButton} from '@angular/material/button';
|
import {MatButton} from '@angular/material/button';
|
||||||
import {ActivatedRoute} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
import {createQrCodeValue} from '../_util/qrCode';
|
import {createQrCodeValue} from '../_util/qrCode';
|
||||||
import {AppDefaults} from '../models/appDefaults.interface';
|
import {AppDefaults} from '../models/appDefaults.interface';
|
||||||
import {LabelLayout} from '../models/labelLayout.interface';
|
import {LabelLayout} from '../models/labelLayout.interface';
|
||||||
@ -26,6 +26,7 @@ export class PrintComponent implements AfterViewInit {
|
|||||||
constructor(
|
constructor(
|
||||||
private api: ApiService,
|
private api: ApiService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
|
private router: Router,
|
||||||
private changeDetector: ChangeDetectorRef,
|
private changeDetector: ChangeDetectorRef,
|
||||||
private settingsService: SettingsService,
|
private settingsService: SettingsService,
|
||||||
private cacheService: CacheService,
|
private cacheService: CacheService,
|
||||||
@ -33,13 +34,14 @@ export class PrintComponent implements AfterViewInit {
|
|||||||
this.dateCreated = new Date();
|
this.dateCreated = new Date();
|
||||||
this.route.queryParamMap.subscribe(queryParamMap => {
|
this.route.queryParamMap.subscribe(queryParamMap => {
|
||||||
this.barCode = queryParamMap.get('barCode');
|
this.barCode = queryParamMap.get('barCode');
|
||||||
this.initials = queryParamMap.get('initials');
|
this.initials = queryParamMap.get('initials').toUpperCase();
|
||||||
});
|
});
|
||||||
this.settings = this.settingsService.getSettings();
|
this.settings = this.settingsService.getSettings();
|
||||||
this.isSaved = false;
|
this.isSaved = false;
|
||||||
|
|
||||||
this.save(s => {
|
this.save(s => {
|
||||||
this.isSaved = true;
|
this.isSaved = true;
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,8 +104,7 @@ export class PrintComponent implements AfterViewInit {
|
|||||||
saveAndPrint() {
|
saveAndPrint() {
|
||||||
this.save(s => {
|
this.save(s => {
|
||||||
window.print();
|
window.print();
|
||||||
this.doneButton.focus();
|
this.router.navigate(['/']);
|
||||||
this.changeDetector.detectChanges();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,20 +45,14 @@
|
|||||||
color="accent"
|
color="accent"
|
||||||
[disabled]="hasErrors"
|
[disabled]="hasErrors"
|
||||||
(click)="goPrint()"
|
(click)="goPrint()"
|
||||||
fxFlex="33%"
|
fxFlex="50%"
|
||||||
>Next <mat-icon>navigate_next</mat-icon></button>
|
>Next <mat-icon>navigate_next</mat-icon></button>
|
||||||
<button
|
<button
|
||||||
mat-flat-button
|
mat-flat-button
|
||||||
class="btn-lg"
|
class="btn-lg"
|
||||||
(click)="resetForm()"
|
(click)="resetForm()"
|
||||||
fxFlex="33%"
|
fxFlex="50%"
|
||||||
><mat-icon>restore</mat-icon> Reset</button>
|
><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-actions>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export class SampleComponent implements AfterViewInit {
|
|||||||
get queryParams(): Params {
|
get queryParams(): Params {
|
||||||
return {
|
return {
|
||||||
barCode: this.barCodeValue.slice(0, 9),
|
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 {
|
get initialsValue(): string {
|
||||||
return this.initialsFormControl.value;
|
return this.initialsFormControl.value.toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasBarCode(): boolean {
|
get hasBarCode(): boolean {
|
||||||
@ -58,7 +58,7 @@ export class SampleComponent implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get hasInitials(): boolean {
|
get hasInitials(): boolean {
|
||||||
return this.settings.initialsRegExp.test(this.initialsValue);
|
return this.settings.initialsRegExp.test(this.initialsValue.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasErrors(): boolean {
|
get hasErrors(): boolean {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import {Injectable} from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import createClone from 'rfdc';
|
import createClone from 'rfdc';
|
||||||
|
import {BehaviorSubject} from 'rxjs';
|
||||||
import serializeJs from 'serialize-javascript';
|
import serializeJs from 'serialize-javascript';
|
||||||
import {defaultOptions} from '../config/defaults';
|
import {defaultOptions} from '../config/defaults';
|
||||||
import {AppDefaults, AppDefaultsOptions} from '../models/appDefaults.interface';
|
import {AppDefaults, AppDefaultsOptions} from '../models/appDefaults.interface';
|
||||||
@ -11,10 +12,15 @@ export class SettingsService {
|
|||||||
// Default form field and data values
|
// Default form field and data values
|
||||||
defaults: AppDefaults = new AppDefaults(defaultOptions);
|
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
|
// Deserializes settings from local storage and returns AppDefaults instance
|
||||||
getStoredSettings(): AppDefaults {
|
getStoredSettings(): AppDefaults {
|
||||||
// tslint:disable-next-line:no-eval
|
// 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
|
// Returns true if settings are found in local storage
|
||||||
@ -40,6 +46,7 @@ export class SettingsService {
|
|||||||
});
|
});
|
||||||
|
|
||||||
localStorage.setItem('settings', serializeJs(settings));
|
localStorage.setItem('settings', serializeJs(settings));
|
||||||
|
this.settings.next(settings);
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,13 +10,14 @@
|
|||||||
</mat-card-header>
|
</mat-card-header>
|
||||||
|
|
||||||
<mat-card-content fxLayout="row wrap" fxLayoutAlign="center center">
|
<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>
|
<mat-label>Location ID #</mat-label>
|
||||||
<input
|
<input
|
||||||
#locationIdInput="matInput"
|
#locationIdInput="matInput"
|
||||||
matInput
|
matInput
|
||||||
type="text"
|
type="text"
|
||||||
[formControl]="locationIdFormControl"
|
[formControl]="locationIdFormControl"
|
||||||
|
(keyup.enter)="save()"
|
||||||
>
|
>
|
||||||
<mat-error *ngIf="locationIdFormControl.hasError('required')">This field is required.</mat-error>
|
<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 exactly 4 digits.</mat-error>
|
||||||
@ -27,7 +28,19 @@
|
|||||||
#labelLayoutSelect="matSelect"
|
#labelLayoutSelect="matSelect"
|
||||||
[formControl]="labelLayoutFormControl"
|
[formControl]="labelLayoutFormControl"
|
||||||
>
|
>
|
||||||
<mat-option *ngFor="let layout of labelLayouts" [value]="layout.type">{{layout.name}}</mat-option>
|
<mat-option *ngFor="let layout of labelLayouts" [value]="layout.id" [ngStyle]="{height: 'inherit', width: 'inherit'}">
|
||||||
|
<div fxLayout="row" fxLayoutAlign="center center" fxLayoutGap="40px" (click)="selectLabelLayout(layout)">
|
||||||
|
<div fxFlex="50%" [ngStyle]="{whiteSpace: 'normal', lineHeight: '1.2'}" (click)="selectLabelLayout(layout)">
|
||||||
|
{{layout.name}}
|
||||||
|
</div>
|
||||||
|
<div fxFlex="50%" (click)="selectLabelLayout(layout)">
|
||||||
|
<app-circle-qrcode-single *ngIf="layout.id === 'circle_qrcode_single'" [sample]="fakeSample"></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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
<mat-error *ngIf="labelLayoutFormControl.hasError('required')">This field is required.</mat-error>
|
<mat-error *ngIf="labelLayoutFormControl.hasError('required')">This field is required.</mat-error>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
@ -38,6 +51,7 @@
|
|||||||
matInput
|
matInput
|
||||||
type="number"
|
type="number"
|
||||||
[formControl]="numCopiesFormControl"
|
[formControl]="numCopiesFormControl"
|
||||||
|
(keyup.enter)="save()"
|
||||||
>
|
>
|
||||||
<mat-error *ngIf="numCopiesFormControl.hasError('required')">This field is required.</mat-error>
|
<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>
|
<mat-error *ngIf="numCopiesFormControl.hasError('pattern')">Please enter only 2-5 letters.</mat-error>
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import {Component, OnInit} from '@angular/core';
|
import {AfterViewInit, ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
|
||||||
import {FormControl, Validators} from '@angular/forms';
|
import {FormControl, Validators} from '@angular/forms';
|
||||||
|
import {MatInput} from '@angular/material/input';
|
||||||
import {Router} from '@angular/router';
|
import {Router} from '@angular/router';
|
||||||
|
import {createQrCodeValue} from '../_util/qrCode';
|
||||||
import {labelLayouts} from '../config/defaults';
|
import {labelLayouts} from '../config/defaults';
|
||||||
import {AppDefaults} from '../models/appDefaults.interface';
|
import {AppDefaults} from '../models/appDefaults.interface';
|
||||||
import {LabelLayout} from '../models/labelLayout.interface';
|
import {LabelLayout} from '../models/labelLayout.interface';
|
||||||
|
import {Sample} from '../models/sample.interface';
|
||||||
import {SettingsService} from '../services/settings.service';
|
import {SettingsService} from '../services/settings.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -11,23 +14,28 @@ import {SettingsService} from '../services/settings.service';
|
|||||||
templateUrl: './settings.component.html',
|
templateUrl: './settings.component.html',
|
||||||
styleUrls: ['./settings.component.scss']
|
styleUrls: ['./settings.component.scss']
|
||||||
})
|
})
|
||||||
export class SettingsComponent implements OnInit {
|
export class SettingsComponent implements AfterViewInit {
|
||||||
settings: AppDefaults;
|
settings: AppDefaults;
|
||||||
numCopiesFormControl: FormControl;
|
numCopiesFormControl: FormControl;
|
||||||
labelLayoutFormControl: FormControl;
|
labelLayoutFormControl: FormControl;
|
||||||
locationIdFormControl: FormControl;
|
locationIdFormControl: FormControl;
|
||||||
labelLayouts: LabelLayout[];
|
labelLayouts: LabelLayout[];
|
||||||
|
fakeSample: Sample;
|
||||||
|
fakeBarcodeValue: string;
|
||||||
|
|
||||||
|
@ViewChild('locationIdInput') locationIdInput: MatInput;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private settingsService: SettingsService
|
private settingsService: SettingsService,
|
||||||
|
private changeDetector: ChangeDetectorRef,
|
||||||
) {
|
) {
|
||||||
this.settings = this.settingsService.getSettings();
|
this.settings = this.settingsService.getSettings();
|
||||||
this.numCopiesFormControl = new FormControl(this.settings.numCopies, [
|
this.numCopiesFormControl = new FormControl(this.settings.numCopies, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.labelLayoutFormControl = new FormControl(this.settings.labelLayout.type, [
|
this.labelLayoutFormControl = new FormControl(this.settings.labelLayout.id, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -37,21 +45,51 @@ export class SettingsComponent implements OnInit {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
this.labelLayouts = Object.values(labelLayouts);
|
this.labelLayouts = Object.values(labelLayouts);
|
||||||
|
|
||||||
|
this._loadFakeData();
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasInfo(): boolean {
|
get hasInfo(): boolean {
|
||||||
return this.numCopiesFormControl.valid && this.locationIdFormControl.valid;
|
return this.numCopiesFormControl.valid && this.locationIdFormControl.valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngAfterViewInit(): void {
|
||||||
|
this.locationIdInput.focus();
|
||||||
|
this.changeDetector.detectChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
this.settingsService.saveSettings({
|
if (this.hasInfo) {
|
||||||
labelLayout: labelLayouts[this.labelLayoutFormControl.value],
|
this.settingsService.saveSettings({
|
||||||
numCopies: this.numCopiesFormControl.value,
|
labelLayout: labelLayouts[this.labelLayoutFormControl.value],
|
||||||
locationId: this.locationIdFormControl.value,
|
numCopies: this.numCopiesFormControl.value,
|
||||||
});
|
locationId: this.locationIdFormControl.value,
|
||||||
this.router.navigate(['/']);
|
});
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make some fake data for sample barcodes
|
||||||
|
private _loadFakeData() {
|
||||||
|
this.fakeSample = {
|
||||||
|
barcode: '',
|
||||||
|
student_id: '123456789',
|
||||||
|
initials: 'ABCDE',
|
||||||
|
date: new Date(),
|
||||||
|
location: this.settings.locationId,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.fakeBarcodeValue = createQrCodeValue(
|
||||||
|
this.fakeSample.student_id,
|
||||||
|
this.fakeSample.initials,
|
||||||
|
this.fakeSample.date,
|
||||||
|
this.fakeSample.location
|
||||||
|
);
|
||||||
|
|
||||||
|
this.fakeSample.barcode = this.fakeBarcodeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectLabelLayout(layout: LabelLayout) {
|
||||||
|
this.labelLayoutFormControl.patchValue(layout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
src/app/testing/sample.mock.ts
Normal file
9
src/app/testing/sample.mock.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {Sample} from '../models/sample.interface';
|
||||||
|
|
||||||
|
export const mockSample: Sample = {
|
||||||
|
barcode: '123456789-ABCDE-202101231234-0101',
|
||||||
|
student_id: '123456789',
|
||||||
|
date: new Date(),
|
||||||
|
initials: 'ABCDE',
|
||||||
|
location: '0101',
|
||||||
|
};
|
BIN
src/assets/bwip-fonts/Inconsolata.otf
Normal file
BIN
src/assets/bwip-fonts/Inconsolata.otf
Normal file
Binary file not shown.
BIN
src/assets/bwip-fonts/OCRA7.ttf
Normal file
BIN
src/assets/bwip-fonts/OCRA7.ttf
Normal file
Binary file not shown.
BIN
src/assets/bwip-fonts/OCRB7.ttf
Normal file
BIN
src/assets/bwip-fonts/OCRB7.ttf
Normal file
Binary file not shown.
@ -442,4 +442,31 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
svg.label-layout-format {
|
||||||
|
text, tspan {
|
||||||
|
font-style: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
font-stretch: normal;
|
||||||
|
font-size: 2.11666656px; // 6pt
|
||||||
|
line-height: 1.25;
|
||||||
|
font-family: monospace;
|
||||||
|
text-align: center;
|
||||||
|
font-variant-ligatures: normal;
|
||||||
|
font-variant-caps: normal;
|
||||||
|
font-variant-numeric: normal;
|
||||||
|
font-feature-settings: normal;
|
||||||
|
font-variant-numeric: normal;
|
||||||
|
font-feature-settings: normal;
|
||||||
|
text-anchor: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.label-layout-border {
|
||||||
|
stroke: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user