Add notes scouting page to scouting app.

Change-Id: Ie2c60ba4a69ae68f407cb2a0c0fb6e90b7bfe1c5
Signed-off-by: Alex Perry <alex.perry96@gmail.com>
diff --git a/scouting/www/BUILD b/scouting/www/BUILD
index f386f06..99dcde6 100644
--- a/scouting/www/BUILD
+++ b/scouting/www/BUILD
@@ -20,6 +20,7 @@
         "//scouting/www/entry",
         "//scouting/www/import_match_list",
         "//scouting/www/match_list",
+        "//scouting/www/notes",
         "@npm//@angular/animations",
         "@npm//@angular/common",
         "@npm//@angular/core",
diff --git a/scouting/www/app.ng.html b/scouting/www/app.ng.html
index fc21164..ac9ef03 100644
--- a/scouting/www/app.ng.html
+++ b/scouting/www/app.ng.html
@@ -11,6 +11,9 @@
     <a class="nav-link" [class.active]="tabIs('Entry')" (click)="switchTabToGuarded('Entry')">Data Entry</a>
   </li>
   <li class="nav-item">
+    <a class="nav-link" [class.active]="tabIs('Notes')" (click)="switchTabToGuarded('Notes')">Notes</a>
+  </li>
+  <li class="nav-item">
     <a class="nav-link" [class.active]="tabIs('ImportMatchList')" (click)="switchTabToGuarded('ImportMatchList')">Import Match List</a>
   </li>
 </ul>
@@ -18,5 +21,6 @@
 <ng-container [ngSwitch]="tab">
   <app-match-list (selectedTeamEvent)="selectTeamInMatch($event)" *ngSwitchCase="'MatchList'"></app-match-list>
   <app-entry (switchTabsEvent)="switchTabTo($event)" [teamNumber]="selectedTeamInMatch.teamNumber" [matchNumber]="selectedTeamInMatch.matchNumber" *ngSwitchCase="'Entry'"></app-entry>
+  <frc971-notes *ngSwitchCase="'Notes'"></frc971-notes>
   <app-import-match-list *ngSwitchCase="'ImportMatchList'"></app-import-match-list>
 </ng-container>
diff --git a/scouting/www/app.ts b/scouting/www/app.ts
index fbde74f..c82fbb3 100644
--- a/scouting/www/app.ts
+++ b/scouting/www/app.ts
@@ -1,6 +1,6 @@
 import {Component, ElementRef, ViewChild} from '@angular/core';
 
-type Tab = 'MatchList'|'Entry'|'ImportMatchList';
+type Tab = 'MatchList'|'Notes'|'Entry'|'ImportMatchList';
 type TeamInMatch = {
   teamNumber: number,
   matchNumber: number,
diff --git a/scouting/www/app_module.ts b/scouting/www/app_module.ts
index e560bfc..e8518d1 100644
--- a/scouting/www/app_module.ts
+++ b/scouting/www/app_module.ts
@@ -1,11 +1,12 @@
 import {NgModule} from '@angular/core';
 import {BrowserModule} from '@angular/platform-browser';
 import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
+
+import {App} from './app';
 import {EntryModule} from './entry/entry.module';
 import {ImportMatchListModule} from './import_match_list/import_match_list.module';
 import {MatchListModule} from './match_list/match_list.module';
-
-import {App} from './app';
+import {NotesModule} from './notes/notes.module';
 
 @NgModule({
   declarations: [App],
@@ -13,6 +14,7 @@
     BrowserModule,
     BrowserAnimationsModule,
     EntryModule,
+    NotesModule,
     ImportMatchListModule,
     MatchListModule,
   ],
diff --git a/scouting/www/entry/entry.component.ts b/scouting/www/entry/entry.component.ts
index 253c0dc..d067c3e 100644
--- a/scouting/www/entry/entry.component.ts
+++ b/scouting/www/entry/entry.component.ts
@@ -2,8 +2,8 @@
 import {FormsModule} from '@angular/forms';
 import {Builder, ByteBuffer} from 'flatbuffers';
 import {ErrorResponse} from 'org_frc971/scouting/webserver/requests/messages/error_response_generated';
-import {SubmitDataScoutingResponse} from 'org_frc971/scouting/webserver/requests/messages/submit_data_scouting_response_generated';
 import {SubmitDataScouting} from 'org_frc971/scouting/webserver/requests/messages/submit_data_scouting_generated';
+import {SubmitDataScoutingResponse} from 'org_frc971/scouting/webserver/requests/messages/submit_data_scouting_response_generated';
 
 type Section = 'Team Selection'|'Auto'|'TeleOp'|'Climb'|'Other'|
     'Review and Submit'|'Success';
diff --git a/scouting/www/notes/BUILD b/scouting/www/notes/BUILD
new file mode 100644
index 0000000..39a97ef
--- /dev/null
+++ b/scouting/www/notes/BUILD
@@ -0,0 +1,29 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_library")
+
+ts_library(
+    name = "notes",
+    srcs = [
+        "notes.component.ts",
+        "notes.module.ts",
+    ],
+    angular_assets = [
+        "notes.component.css",
+        "notes.ng.html",
+        "//scouting/www:common_css",
+    ],
+    compiler = "//tools:tsc_wrapped_with_angular",
+    target_compatible_with = ["@platforms//cpu:x86_64"],
+    use_angular_plugin = True,
+    visibility = ["//visibility:public"],
+    deps = [
+        "//scouting/webserver/requests/messages:error_response_ts_fbs",
+        "//scouting/webserver/requests/messages:request_notes_for_team_response_ts_fbs",
+        "//scouting/webserver/requests/messages:request_notes_for_team_ts_fbs",
+        "//scouting/webserver/requests/messages:submit_notes_response_ts_fbs",
+        "//scouting/webserver/requests/messages:submit_notes_ts_fbs",
+        "@com_github_google_flatbuffers//ts:flatbuffers_ts",
+        "@npm//@angular/common",
+        "@npm//@angular/core",
+        "@npm//@angular/forms",
+    ],
+)
diff --git a/scouting/www/notes/notes.component.css b/scouting/www/notes/notes.component.css
new file mode 100644
index 0000000..869bdab
--- /dev/null
+++ b/scouting/www/notes/notes.component.css
@@ -0,0 +1,12 @@
+.error {
+  color: red;
+}
+
+.text-input {
+  width: calc(100% - 20px);
+}
+
+.buttons {
+  display: flex;
+  justify-content: space-between;
+}
diff --git a/scouting/www/notes/notes.component.ts b/scouting/www/notes/notes.component.ts
new file mode 100644
index 0000000..09178f8
--- /dev/null
+++ b/scouting/www/notes/notes.component.ts
@@ -0,0 +1,90 @@
+import {Component} from '@angular/core';
+import {Builder, ByteBuffer} from 'flatbuffers';
+import {ErrorResponse} from 'org_frc971/scouting/webserver/requests/messages/error_response_generated';
+import {RequestNotesForTeam} from 'org_frc971/scouting/webserver/requests/messages/request_notes_for_team_generated';
+import {Note as NoteFb, RequestNotesForTeamResponse} from 'org_frc971/scouting/webserver/requests/messages/request_notes_for_team_response_generated';
+import {SubmitNotes} from 'org_frc971/scouting/webserver/requests/messages/submit_notes_generated';
+import {SubmitNotesResponse} from 'org_frc971/scouting/webserver/requests/messages/submit_notes_response_generated';
+
+type Section = 'TeamSelection'|'Data';
+
+interface Note {
+  readonly data: string;
+}
+
+@Component({
+  selector: 'frc971-notes',
+  templateUrl: './notes.ng.html',
+  styleUrls: ['../common.css', './notes.component.css']
+})
+export class Notes {
+  section: Section = 'TeamSelection';
+  notes: Note[] = [];
+
+  errorMessage = '';
+
+  teamNumber: number = 971;
+  newData = '';
+
+  async setTeamNumber() {
+    const builder = new Builder();
+    RequestNotesForTeam.startRequestNotesForTeam(builder);
+    RequestNotesForTeam.addTeam(builder, this.teamNumber);
+    builder.finish(RequestNotesForTeam.endRequestNotesForTeam(builder));
+
+    const buffer = builder.asUint8Array();
+    const res = await fetch(
+        '/requests/request/notes_for_team', {method: 'POST', body: buffer});
+
+    const resBuffer = await res.arrayBuffer();
+    const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
+
+    if (res.ok) {
+      this.notes = [];
+      const parsedResponse =
+          RequestNotesForTeamResponse.getRootAsRequestNotesForTeamResponse(
+              fbBuffer);
+      for (let i = 0; i < parsedResponse.notesLength(); i++) {
+        const fbNote = parsedResponse.notes(i);
+        this.notes.push({data: fbNote.data()});
+      }
+      this.section = 'Data';
+    } else {
+      const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
+
+      const errorMessage = parsedResponse.errorMessage();
+      this.errorMessage =
+          `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
+    }
+  }
+
+  changeTeam() {
+    this.section = "TeamSelection";
+  }
+  
+  async submitData() {
+    const builder = new Builder();
+    const dataFb = builder.createString(this.newData);
+    builder.finish(
+        SubmitNotes.createSubmitNotes(builder, this.teamNumber, dataFb));
+
+    const buffer = builder.asUint8Array();
+    const res = await fetch(
+        '/requests/submit/submit_notes', {method: 'POST', body: buffer});
+
+    if (res.ok) {
+      this.newData = '';
+      this.errorMessage = '';
+      await this.setTeamNumber();
+    } else {
+      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}"`;
+    }
+  }
+}
+
diff --git a/scouting/www/notes/notes.module.ts b/scouting/www/notes/notes.module.ts
new file mode 100644
index 0000000..c537550
--- /dev/null
+++ b/scouting/www/notes/notes.module.ts
@@ -0,0 +1,14 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {FormsModule} from '@angular/forms';
+
+import {Notes} from './notes.component';
+
+@NgModule({
+  declarations: [Notes],
+  exports: [Notes],
+  imports: [CommonModule, FormsModule],
+})
+export class NotesModule {
+}
+
diff --git a/scouting/www/notes/notes.ng.html b/scouting/www/notes/notes.ng.html
new file mode 100644
index 0000000..90f004a
--- /dev/null
+++ b/scouting/www/notes/notes.ng.html
@@ -0,0 +1,26 @@
+<h2>Notes</h2>
+
+<ng-container [ngSwitch]="section">
+  <div *ngSwitchCase="'TeamSelection'">
+    <label for="team_number_notes">Team Number</label>
+    <input [(ngModel)]="teamNumber" type="number" id="team_number_notes" min="1" max="9999">
+    <button class="btn btn-primary" (click)="setTeamNumber()">Select</button>
+  </div>
+
+  <div *ngSwitchCase="'Data'">
+    <h3> Scouting team: {{teamNumber}}</h3>
+    <ul *ngFor="let note of notes">
+      <li class="note">
+        {{ note.data }}
+      </li>
+    </ul>
+    <textarea class="text-input" [(ngModel)]="newData"></textarea>
+    <div class="buttons">
+      <button class="btn btn-primary" (click)="changeTeam()">Change team</button>
+      <button class="btn btn-primary" (click)="submitData()">Submit</button>
+    </div>
+  </div>
+  <div class="error">
+    {{errorMessage}}
+  </div>
+</ng-container>