Add Pit Scouting Tab
Signed-off-by: Emily Markova <emily.markova@gmail.com>
Change-Id: Iede446546e20f2915bb53e134050b5025976da36
diff --git a/scouting/www/pit_scouting/BUILD b/scouting/www/pit_scouting/BUILD
new file mode 100644
index 0000000..740dee1
--- /dev/null
+++ b/scouting/www/pit_scouting/BUILD
@@ -0,0 +1,18 @@
+load("@npm//:defs.bzl", "npm_link_all_packages")
+load("//tools/build_rules:js.bzl", "ng_pkg")
+
+npm_link_all_packages(name = "node_modules")
+
+ng_pkg(
+ name = "pit_scouting",
+ extra_srcs = [
+ "//scouting/www:app_common_css",
+ ],
+ deps = [
+ ":node_modules/@angular/forms",
+ "//scouting/webserver/requests/messages:error_response_ts_fbs",
+ "//scouting/webserver/requests/messages:submit_pit_image_response_ts_fbs",
+ "//scouting/webserver/requests/messages:submit_pit_image_ts_fbs",
+ "@com_github_google_flatbuffers//ts:flatbuffers_ts",
+ ],
+)
diff --git a/scouting/www/pit_scouting/package.json b/scouting/www/pit_scouting/package.json
new file mode 100644
index 0000000..e58fc51
--- /dev/null
+++ b/scouting/www/pit_scouting/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@org_frc971/scouting/www/pit_scouting",
+ "private": true,
+ "dependencies": {
+ "@angular/forms": "15.1.5"
+ }
+}
diff --git a/scouting/www/pit_scouting/pit_scouting.component.css b/scouting/www/pit_scouting/pit_scouting.component.css
new file mode 100644
index 0000000..b224d73
--- /dev/null
+++ b/scouting/www/pit_scouting/pit_scouting.component.css
@@ -0,0 +1,8 @@
+* {
+ padding: 10px;
+}
+
+button {
+ touch-action: manipulation;
+ margin-top: 1vh;
+}
diff --git a/scouting/www/pit_scouting/pit_scouting.component.ts b/scouting/www/pit_scouting/pit_scouting.component.ts
new file mode 100644
index 0000000..b74c119
--- /dev/null
+++ b/scouting/www/pit_scouting/pit_scouting.component.ts
@@ -0,0 +1,75 @@
+import {Component} from '@angular/core';
+import {Builder, ByteBuffer} from 'flatbuffers';
+import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
+import {SubmitPitImage} from '../../webserver/requests/messages/submit_pit_image_generated';
+
+type Section = 'TeamSelection' | 'Data';
+
+interface Input {
+ teamNumber: number;
+ pitImage: HTMLImageElement;
+}
+
+@Component({
+ selector: 'app-pit-scouting',
+ templateUrl: './pit_scouting.ng.html',
+ styleUrls: ['../app/common.css', './pit_scouting.component.css'],
+})
+export class PitScoutingComponent {
+ section: Section = 'Data';
+
+ errorMessage = '';
+ teamNumber: number = 971;
+ pitImage: string = '';
+
+ async readFile(file): Promise<ArrayBuffer> {
+ return new Promise((resolve, reject) => {
+ let reader = new FileReader();
+ reader.addEventListener('loadend', (e) =>
+ resolve(e.target.result as ArrayBuffer)
+ );
+ reader.addEventListener('error', reject);
+ reader.readAsArrayBuffer(file);
+ });
+ }
+
+ async getImageData() {
+ const selectedFile = (<HTMLInputElement>document.getElementById('pitImage'))
+ .files[0];
+ return new Uint8Array(await this.readFile(selectedFile));
+ }
+
+ async submitData() {
+ const builder = new Builder();
+ const teamNumber = builder.createString(this.teamNumber.toString());
+ const pitImage = builder.createString(this.pitImage.toString());
+ const imageData = SubmitPitImage.createImageDataVector(
+ builder,
+ await this.getImageData()
+ );
+ builder.finish(
+ SubmitPitImage.createSubmitPitImage(
+ builder,
+ teamNumber,
+ pitImage,
+ imageData
+ )
+ );
+
+ const buffer = builder.asUint8Array();
+ const res = await fetch('/requests/submit/submit_pit_image', {
+ method: 'POST',
+ body: buffer,
+ });
+ if (!res.ok) {
+ const resBuffer = await res.arrayBuffer();
+ const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
+ const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
+
+ const errorMessage = parsedResponse.errorMessage();
+ this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
+ }
+ this.section = 'TeamSelection';
+ this.pitImage = '';
+ }
+}
diff --git a/scouting/www/pit_scouting/pit_scouting.module.ts b/scouting/www/pit_scouting/pit_scouting.module.ts
new file mode 100644
index 0000000..7d92f6c
--- /dev/null
+++ b/scouting/www/pit_scouting/pit_scouting.module.ts
@@ -0,0 +1,11 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {FormsModule} from '@angular/forms';
+import {PitScoutingComponent} from './pit_scouting.component';
+
+@NgModule({
+ declarations: [PitScoutingComponent],
+ exports: [PitScoutingComponent],
+ imports: [CommonModule, FormsModule],
+})
+export class PitScoutingModule {}
diff --git a/scouting/www/pit_scouting/pit_scouting.ng.html b/scouting/www/pit_scouting/pit_scouting.ng.html
new file mode 100644
index 0000000..98aa313
--- /dev/null
+++ b/scouting/www/pit_scouting/pit_scouting.ng.html
@@ -0,0 +1,30 @@
+<div class="header">
+ <h2>Pit Scouting</h2>
+</div>
+<ng-container [ngSwitch]="section">
+ <div *ngSwitchCase="'Data'" id="pitImageSelection" class="container-fluid">
+ <label for="teamNumber">Team Number</label>
+ <input
+ [(ngModel)]="teamNumber"
+ type="string"
+ id="teamNumber"
+ min="1"
+ max="9999"
+ />
+ <form action="setPitImage()">
+ <label for="pitImage">Select pit image:</label>
+ <br />
+ <input
+ id="pitImage"
+ [(ngModel)]="pitImage"
+ type="file"
+ accept="image/*"
+ />
+ </form>
+ <button id="submit-button" class="btn btn-success" (click)="submitData()">
+ Submit
+ </button>
+ </div>
+
+ <span class="error_message" role="alert">{{ errorMessage }}</span>
+</ng-container>