diff --git a/package-lock.json b/package-lock.json index f91a41a..0fd8d31 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4610,6 +4610,22 @@ } } }, + "jasmine": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-4.0.2.tgz", + "integrity": "sha512-YsrgxJQEggxzByYe4j68eQLOiQeSrPDYGv4sHhGBp3c6HHdq+uPXeAQ73kOAQpdLZ3/0zN7x/TZTloqeE1/qIA==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "^4.0.0" + } + }, + "jasmine-core": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.0.1.tgz", + "integrity": "sha512-w+JDABxQCkxbGGxg+a2hUVZyqUS2JKngvIyLGu/xiw2ZwgsoSB0iiecLQsQORSeaKQ6iGrCyWG86RfNDuoA7Lg==", + "dev": true + }, "jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", diff --git a/package.json b/package.json index 041ff88..377a52f 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "scripts": { "ng": "ng", + "test": "jasmine", "start": "ng serve", "build": "ng build", "watch": "ng build --watch --configuration development" @@ -26,6 +27,7 @@ "@angular/cli": "~13.2.2", "@angular/compiler-cli": "~13.2.0", "@types/node": "^12.11.1", + "jasmine": "^4.0.2", "typescript": "~4.5.2" } } diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..6afe603 --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,13 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.?(m)js" + ], + "helpers": [ + "helpers/**/*.?(m)js" + ], + "env": { + "stopSpecOnExpectationFailure": false, + "random": true + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts new file mode 100644 index 0000000..d703db0 --- /dev/null +++ b/src/app/app-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { HomeComponent } from './home.component'; +import { ReportComponent } from './report.component'; +import { ThanksComponent } from './thanks.component'; + +const routes: Routes = [ + { path: '', component: HomeComponent }, + { path: 'thanks', component: ThanksComponent }, + { path: 'report', component: ReportComponent } +]; + +@NgModule({ + imports: [RouterModule.forRoot(routes)], + exports: [RouterModule] +}) +export class AppRoutingModule { } diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 2a5ce6c..51b5ece 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,39 +1,13 @@ import { Component } from '@angular/core'; -import { checkin, checkout } from "src/assets/forms"; - @Component({ selector: 'app-root', template: ` - - - - - - - + ` }) export class AppComponent { - - checkin: Form = checkin; - checkout: Form = checkout; - - action: string = "checkout"; ngOnInit() { } - switch(action: string) { - - alert("Are you sure?"); - - this.action = action; - - } - } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b79eb98..13af438 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -7,6 +7,12 @@ import { FormComponent } from './form.component'; import { TextComponent } from './text.component'; import { RadioComponent } from './radio.component'; import { StepComponent } from './step.component'; +import { CameraComponent } from './camera.component'; +import { SignComponent } from './sign.component'; +import { ThanksComponent } from './thanks.component'; +import { AppRoutingModule } from './app-routing.module'; +import { HomeComponent } from './home.component'; +import { ReportComponent } from './report.component'; @NgModule({ declarations: [ @@ -15,10 +21,16 @@ import { StepComponent } from './step.component'; TextComponent, RadioComponent, StepComponent, + CameraComponent, + SignComponent, + ThanksComponent, + HomeComponent, + ReportComponent, ], imports: [ BrowserModule, FormsModule, + AppRoutingModule, ], providers: [], bootstrap: [AppComponent] diff --git a/src/app/camera.component.ts b/src/app/camera.component.ts new file mode 100644 index 0000000..a05b708 --- /dev/null +++ b/src/app/camera.component.ts @@ -0,0 +1,58 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'app-camera', + template: ` +
+ + + + + + + +
+ ` +}) +export class CameraComponent implements OnInit { + + @Input() field: Field; + @Output() update: EventEmitter = new EventEmitter(); + + video: HTMLVideoElement; + + ngOnInit() { console.log("Camera", this.field.id); } + + ngAfterViewInit() { + + this.video = document.getElementById("video") as HTMLVideoElement; + + navigator.mediaDevices.getUserMedia({video: true, audio: false}) + + .then((stream) => { + this.video.srcObject = stream; + this.video.play(); + + }); + + } + + capture() { + + const canvas = document.createElement("canvas") as HTMLCanvasElement; + + canvas.width = this.video.videoWidth; + canvas.height = this.video.videoHeight; + + canvas + .getContext("2d") + .drawImage(this.video, 0, 0, canvas.width, canvas.height); + + this.field.ok = true; + this.field.value = canvas.toDataURL(); + + this.update.emit(); + + } + +} diff --git a/src/app/form.component.ts b/src/app/form.component.ts index 699009d..7347f4f 100644 --- a/src/app/form.component.ts +++ b/src/app/form.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, Input } from '@angular/core'; +import { Router } from '@angular/router'; @Component({ selector: 'app-form', @@ -15,6 +16,7 @@ import { Component, OnInit, Input } from '@angular/core'; > + + + + + + + + + Go To Report Page + + + ` +}) +export class HomeComponent implements OnInit { + + checkin: Form = checkin; + checkout: Form = checkout; + + action: string = "checkout"; + + ngOnInit() { } + + switch(action: string) { + + alert("Are you sure?"); + + this.action = action; + + } + +} diff --git a/src/app/radio.component.ts b/src/app/radio.component.ts index 4c5e014..32833d9 100644 --- a/src/app/radio.component.ts +++ b/src/app/radio.component.ts @@ -25,7 +25,7 @@ export class RadioComponent implements OnInit { @Input() field: Field; @Output() update: EventEmitter = new EventEmitter(); - ngOnInit() { console.log("Text", this.field.id); } + ngOnInit() { console.log("Radio", this.field.id); } check() { diff --git a/src/app/report.component.ts b/src/app/report.component.ts new file mode 100644 index 0000000..b6ec342 --- /dev/null +++ b/src/app/report.component.ts @@ -0,0 +1,53 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-report', + template: ` +
+ + + + + +
+ + {{ entry.name }} + +
+ +
+ +
+ `, + styles: [ + ] +}) +export class ReportComponent implements OnInit { + + data: Entry[]; + + ngOnInit() { + + this.data = data; + + // try { + + // const res = await fetch("url"); + + // } catch(e) { alert("Something went wrong."); } + + // this.data = res.json(); + + } + +} + + +/* Placeholder data */ +const data: Entry[] = [{ + name: "John Doe", + eid: "john.doe", + asset: "monitor", + complete: false, + photo: "./assets/monitor.jfif" +}] diff --git a/src/app/sign.component.ts b/src/app/sign.component.ts new file mode 100644 index 0000000..87d9a87 --- /dev/null +++ b/src/app/sign.component.ts @@ -0,0 +1,72 @@ +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'app-sign', + template: ` +
+ + + + + +
+ ` +}) +export class SignComponent implements OnInit { + + @Input() field: Field; + @Output() update: EventEmitter = new EventEmitter(); + + pad: HTMLCanvasElement; + + ngOnInit() { console.log("Sign", this.field.id); } + + ngAfterViewInit() { + + this.pad = document.getElementById("pad") as HTMLCanvasElement; + + const ctx = this.pad.getContext('2d'); + + this.pad.width = 300; + this.pad.height = 100; + + ctx.lineWidth = 3; + ctx.lineJoin = 'round'; + ctx.lineCap = 'round'; + + let active, cursor; + + this.pad.onmousedown = (e) => { + active = true; + cursor = { x: e.offsetX, y: e.offsetY }; + } + + this.pad.onmousemove = (e) => { + if (active) { + ctx.beginPath(); + ctx.moveTo(cursor.x, cursor.y); + ctx.lineTo(e.offsetX, e.offsetY); + ctx.stroke(); + cursor = { x: e.offsetX, y: e.offsetY }; + } + } + + this.pad.onmouseup = (_e) => { + active = false; + this.field.value = this.pad.toDataURL(); + this.field.ok = true; + this.update.emit(); + }; + + } + + clear() { + + this.pad.getContext('2d').clearRect(0, 0, this.pad.width, this.pad.height); + this.field.value = null; + this.field.ok = false; + this.update.emit(); + + }; + +} diff --git a/src/app/step.component.ts b/src/app/step.component.ts index 1cdaa91..813709c 100644 --- a/src/app/step.component.ts +++ b/src/app/step.component.ts @@ -23,6 +23,20 @@ import { Component, OnInit, Input } from '@angular/core'; > + + + + @@ -32,13 +46,10 @@ export class StepComponent implements OnInit { @Input() step: Step; - ngOnInit() { console.log("Step", this.step); } + ngOnInit() { console.log("Step", this.step.prompt); } update() { - console.log(this.step.fields); - - /* Check for completeness */ for (let field of this.step.fields) { if (!field.ok) { this.step.ok = false; diff --git a/src/app/thanks.component.ts b/src/app/thanks.component.ts new file mode 100644 index 0000000..d3e4a2b --- /dev/null +++ b/src/app/thanks.component.ts @@ -0,0 +1,22 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-thanks', + template: ` +

+ Thank-You! + + Go Home +

+ `, + styles: [ + ] +}) +export class ThanksComponent implements OnInit { + + constructor() { } + + ngOnInit(): void { + } + +} diff --git a/src/assets/forms.ts b/src/assets/forms.ts index 0dbe1b1..0c957c4 100644 --- a/src/assets/forms.ts +++ b/src/assets/forms.ts @@ -35,6 +35,28 @@ export const checkout: Form = { options: { choices: ["mouse", "keyboard", "monitor", "chair"] } }] }, + { + ok: false, + prompt: "Take a photo.", + fields: [{ + ok: false, + type: "camera", + id: "photo", + value: null, + options: null + }] + }, + { + ok: false, + prompt: "Sign your name.", + fields: [{ + ok: false, + type: "sign", + id: "signiture", + value: null, + options: null + }] + }, { ok: false, prompt: "Would you like a confirmation email?", @@ -81,13 +103,13 @@ export const checkin: Form = { }, { ok: false, - prompt: "Would you like a confirmation email?", + prompt: "Sign your name.", fields: [{ ok: false, - type: "radio", - id: "confirmation", + type: "sign", + id: "signiture", value: null, - options: { choices: ["yes", "no"] } + options: null }] }] diff --git a/src/assets/monitor.jfif b/src/assets/monitor.jfif new file mode 100644 index 0000000..2ed1cca Binary files /dev/null and b/src/assets/monitor.jfif differ diff --git a/src/styles.css b/src/styles.css index 9f41362..51cf013 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,5 +1,18 @@ +.bar { + pointer-events: none; +} + .active { pointer-events: auto; - color: blue; border-color: blue; -} \ No newline at end of file + color: blue; +} + +video, img { + width: 100px; +} + +#pad { + background-color: white; + border: 2px solid black; +} diff --git a/src/types.d.ts b/src/types.d.ts index 7f8c2c5..35b221c 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -1,11 +1,3 @@ -declare interface TextOptions { - modifier: string; -} - -declare interface RadioOptions { - choices: string[]; -} - declare interface Field { type: string; id: string; @@ -26,4 +18,12 @@ declare interface Form { goto: string; step: number; steps: Step[]; -} +} + +declare interface Entry { + name: string; + eid: string; + asset: string; + complete: boolean; + photo: string; +}