Add 2023 scouting interface

Adds a basic, functional interface to submit data scouting for
the new 2023 game using our new 'actions architecture'.

Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: I2c7d65e21daa6c3b9f7a647e3a808874f7b3be8a
diff --git a/scouting/www/entry/BUILD b/scouting/www/entry/BUILD
index 37b7fe6..07c1d79 100644
--- a/scouting/www/entry/BUILD
+++ b/scouting/www/entry/BUILD
@@ -11,9 +11,7 @@
     deps = [
         ":node_modules/@angular/forms",
         "//scouting/webserver/requests/messages:error_response_ts_fbs",
-        "//scouting/webserver/requests/messages:submit_data_scouting_response_ts_fbs",
-        "//scouting/webserver/requests/messages:submit_data_scouting_ts_fbs",
-        "//scouting/www/counter_button:_lib",
+        "//scouting/webserver/requests/messages:submit_actions_ts_fbs",
         "@com_github_google_flatbuffers//ts:flatbuffers_ts",
     ],
 )
diff --git a/scouting/www/entry/entry.component.ts b/scouting/www/entry/entry.component.ts
index 6209669..9aae0d1 100644
--- a/scouting/www/entry/entry.component.ts
+++ b/scouting/www/entry/entry.component.ts
@@ -11,17 +11,25 @@
 import {Builder, ByteBuffer} from 'flatbuffers';
 import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
 import {
-  ClimbLevel,
-  SubmitDataScouting,
-} from '../../webserver/requests/messages/submit_data_scouting_generated';
-import {SubmitDataScoutingResponse} from '../../webserver/requests/messages/submit_data_scouting_response_generated';
+  ObjectType,
+  ScoreLevel,
+  SubmitActions,
+  StartMatchAction,
+  PickupObjectAction,
+  PlaceObjectAction,
+  RobotDeathAction,
+  EndMatchAction,
+  ActionType,
+  Action,
+} from '../../webserver/requests/messages/submit_actions_generated';
 
 type Section =
   | 'Team Selection'
-  | 'Auto'
-  | 'TeleOp'
-  | 'Climb'
-  | 'Other'
+  | 'Init'
+  | 'Pickup'
+  | 'Place'
+  | 'Endgame'
+  | 'Dead'
   | 'Review and Submit'
   | 'Success';
 
@@ -38,22 +46,42 @@
   f: 'Finals',
 };
 
-const IMAGES_ARRAY = [
-  {
-    id: 'field_quadrants_image',
-    original_image:
-      '/sha256/cbb99a057a2504e80af526dae7a0a04121aed84c56a6f4889e9576fe1c20c61e/pictures/field/quadrants.jpeg',
-    reversed_image:
-      '/sha256/ee4d24cf6b850158aa64e2b301c31411cb28f88a247a8916abb97214bb251eb5/pictures/field/reversed_quadrants.jpeg',
-  },
-  {
-    id: 'field_balls_image',
-    original_image:
-      '/sha256/e095cc8a75d804b0e2070e0a941fab37154176756d4c1a775e53cc48c3a732b9/pictures/field/balls.jpeg',
-    reversed_image:
-      '/sha256/fe4a4605c03598611c583d4dcdf28e06a056a17302ae91f5c527568966d95f3a/pictures/field/reversed_balls.jpeg',
-  },
-];
+type ActionT =
+  | {
+      type: 'startMatchAction';
+      timestamp?: number;
+      position: number;
+    }
+  | {
+      type: 'pickupObjectAction';
+      timestamp?: number;
+      objectType: ObjectType;
+      auto?: boolean;
+    }
+  | {
+      type: 'placeObjectAction';
+      timestamp?: number;
+      objectType?: ObjectType;
+      scoreLevel: ScoreLevel;
+      auto?: boolean;
+    }
+  | {
+      type: 'robotDeathAction';
+      timestamp?: number;
+      robotOn: boolean;
+    }
+  | {
+      type: 'endMatchAction';
+      docked: boolean;
+      engaged: boolean;
+      timestamp?: number;
+    }
+  | {
+      // This is not a action that is submitted,
+      // It is used for undoing purposes.
+      type: 'endAutoPhase';
+      timestamp?: number;
+    };
 
 @Component({
   selector: 'app-entry',
@@ -63,9 +91,10 @@
 export class EntryComponent {
   // Re-export the type here so that we can use it in the `[value]` attribute
   // of radio buttons.
-  readonly ClimbLevel = ClimbLevel;
   readonly COMP_LEVELS = COMP_LEVELS;
   readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
+  readonly ObjectType = ObjectType;
+  readonly ScoreLevel = ScoreLevel;
 
   section: Section = 'Team Selection';
   @Output() switchTabsEvent = new EventEmitter<string>();
@@ -73,118 +102,169 @@
   @Input() teamNumber: number = 1;
   @Input() setNumber: number = 1;
   @Input() compLevel: CompLevel = 'qm';
-  autoUpperShotsMade: number = 0;
-  autoLowerShotsMade: number = 0;
-  autoShotsMissed: number = 0;
-  teleUpperShotsMade: number = 0;
-  teleLowerShotsMade: number = 0;
-  teleShotsMissed: number = 0;
-  defensePlayedOnScore: number = 0;
-  defensePlayedScore: number = 0;
-  level: ClimbLevel = ClimbLevel.NoAttempt;
-  ball1: boolean = false;
-  ball2: boolean = false;
-  ball3: boolean = false;
-  ball4: boolean = false;
-  ball5: boolean = false;
-  quadrant: number = 1;
+
+  actionList: ActionT[] = [];
   errorMessage: string = '';
-  noShow: boolean = false;
-  neverMoved: boolean = false;
-  batteryDied: boolean = false;
-  mechanicallyBroke: boolean = false;
-  lostComs: boolean = false;
-  comment: string = '';
+  autoPhase: boolean = true;
+  lastObject: ObjectType = null;
+
+  matchStartTimestamp: number = 0;
+
+  addAction(action: ActionT): void {
+    action.timestamp = Math.floor(Date.now() / 1000);
+    if (action.type == 'startMatchAction') {
+      // Unix nanosecond timestamp.
+      this.matchStartTimestamp = Date.now() * 1e6;
+      action.timestamp = 0;
+    } else {
+      // Unix nanosecond timestamp relative to match start.
+      action.timestamp = Date.now() * 1e6 - this.matchStartTimestamp;
+    }
+
+    if (
+      action.type == 'pickupObjectAction' ||
+      action.type == 'placeObjectAction'
+    ) {
+      action.auto = this.autoPhase;
+      if (action.type == 'pickupObjectAction') {
+        this.lastObject = action.objectType;
+      } else if (action.type == 'placeObjectAction') {
+        action.objectType = this.lastObject;
+      }
+    }
+    this.actionList.push(action);
+  }
+
+  undoLastAction() {
+    if (this.actionList.length > 0) {
+      let lastAction = this.actionList.pop();
+      switch (lastAction?.type) {
+        case 'endAutoPhase':
+          this.autoPhase = true;
+        case 'pickupObjectAction':
+          this.section = 'Pickup';
+          break;
+        case 'placeObjectAction':
+          this.section = 'Place';
+          break;
+        case 'endMatchAction':
+          this.section = 'Pickup';
+          break;
+        default:
+          break;
+      }
+    }
+  }
+
+  changeSectionTo(target: Section) {
+    this.section = target;
+  }
 
   @ViewChild('header') header: ElementRef;
 
-  nextSection() {
-    if (this.section === 'Team Selection') {
-      this.section = 'Auto';
-    } else if (this.section === 'Auto') {
-      this.section = 'TeleOp';
-    } else if (this.section === 'TeleOp') {
-      this.section = 'Climb';
-    } else if (this.section === 'Climb') {
-      this.section = 'Other';
-    } else if (this.section === 'Other') {
-      this.section = 'Review and Submit';
-    } else if (this.section === 'Review and Submit') {
-      this.submitDataScouting();
-      return;
-    } else if (this.section === 'Success') {
-      this.switchTabsEvent.emit('MatchList');
-      return;
-    }
-    // Scroll back to the top so that we can be sure the user sees the
-    // entire next screen. Otherwise it's easy to overlook input fields.
-    this.scrollToTop();
-  }
-
-  prevSection() {
-    if (this.section === 'Auto') {
-      this.section = 'Team Selection';
-    } else if (this.section === 'TeleOp') {
-      this.section = 'Auto';
-    } else if (this.section === 'Climb') {
-      this.section = 'TeleOp';
-    } else if (this.section === 'Other') {
-      this.section = 'Climb';
-    } else if (this.section === 'Review and Submit') {
-      this.section = 'Other';
-    }
-    // Scroll back to the top so that we can be sure the user sees the
-    // entire previous screen. Otherwise it's easy to overlook input
-    // fields.
-    this.scrollToTop();
-  }
-
-  flipImages() {
-    for (let obj of IMAGES_ARRAY) {
-      let img = document.getElementById(obj.id) as HTMLImageElement;
-      img.src = img.src.endsWith(obj.original_image)
-        ? obj.reversed_image
-        : obj.original_image;
-    }
-  }
   private scrollToTop() {
     this.header.nativeElement.scrollIntoView();
   }
 
-  async submitDataScouting() {
-    this.errorMessage = '';
-
+  async submitActions() {
     const builder = new Builder();
-    const compLevel = builder.createString(this.compLevel);
-    const comment = builder.createString(this.comment);
-    SubmitDataScouting.startSubmitDataScouting(builder);
-    SubmitDataScouting.addTeam(builder, this.teamNumber);
-    SubmitDataScouting.addMatch(builder, this.matchNumber);
-    SubmitDataScouting.addSetNumber(builder, this.setNumber);
-    SubmitDataScouting.addCompLevel(builder, compLevel);
-    SubmitDataScouting.addMissedShotsAuto(builder, this.autoShotsMissed);
-    SubmitDataScouting.addUpperGoalAuto(builder, this.autoUpperShotsMade);
-    SubmitDataScouting.addLowerGoalAuto(builder, this.autoLowerShotsMade);
-    SubmitDataScouting.addMissedShotsTele(builder, this.teleShotsMissed);
-    SubmitDataScouting.addUpperGoalTele(builder, this.teleUpperShotsMade);
-    SubmitDataScouting.addLowerGoalTele(builder, this.teleLowerShotsMade);
-    SubmitDataScouting.addDefenseRating(builder, this.defensePlayedScore);
-    SubmitDataScouting.addDefenseReceivedRating(
+    const actionOffsets: number[] = [];
+
+    for (const action of this.actionList) {
+      let actionOffset: number | undefined;
+      console.log(action.type);
+
+      switch (action.type) {
+        case 'startMatchAction':
+          const startMatchActionOffset =
+            StartMatchAction.createStartMatchAction(builder, action.position);
+          actionOffset = Action.createAction(
+            builder,
+            action.timestamp || 0,
+            ActionType.StartMatchAction,
+            startMatchActionOffset
+          );
+          break;
+
+        case 'pickupObjectAction':
+          const pickupObjectActionOffset =
+            PickupObjectAction.createPickupObjectAction(
+              builder,
+              action.objectType,
+              action.auto || false
+            );
+          actionOffset = Action.createAction(
+            builder,
+            action.timestamp || 0,
+            ActionType.PickupObjectAction,
+            pickupObjectActionOffset
+          );
+          break;
+
+        case 'placeObjectAction':
+          const placeObjectActionOffset =
+            PlaceObjectAction.createPlaceObjectAction(
+              builder,
+              action.objectType,
+              action.scoreLevel,
+              action.auto || false
+            );
+          actionOffset = Action.createAction(
+            builder,
+            action.timestamp || 0,
+            ActionType.PlaceObjectAction,
+            placeObjectActionOffset
+          );
+          break;
+
+        case 'robotDeathAction':
+          const robotDeathActionOffset =
+            RobotDeathAction.createRobotDeathAction(builder, action.robotOn);
+          actionOffset = Action.createAction(
+            builder,
+            action.timestamp || 0,
+            ActionType.RobotDeathAction,
+            robotDeathActionOffset
+          );
+          break;
+
+        case 'endMatchAction':
+          const endMatchActionOffset = EndMatchAction.createEndMatchAction(
+            builder,
+            action.docked,
+            action.engaged
+          );
+          actionOffset = Action.createAction(
+            builder,
+            action.timestamp || 0,
+            ActionType.EndMatchAction,
+            endMatchActionOffset
+          );
+          break;
+
+        case 'endAutoPhase':
+          // Not important action.
+          break;
+
+        default:
+          throw new Error(`Unknown action type`);
+      }
+
+      if (actionOffset !== undefined) {
+        actionOffsets.push(actionOffset);
+      }
+    }
+
+    const actionsVector = SubmitActions.createActionsListVector(
       builder,
-      this.defensePlayedOnScore
+      actionOffsets
     );
-    SubmitDataScouting.addAutoBall1(builder, this.ball1);
-    SubmitDataScouting.addAutoBall2(builder, this.ball2);
-    SubmitDataScouting.addAutoBall3(builder, this.ball3);
-    SubmitDataScouting.addAutoBall4(builder, this.ball4);
-    SubmitDataScouting.addAutoBall5(builder, this.ball5);
-    SubmitDataScouting.addStartingQuadrant(builder, this.quadrant);
-    SubmitDataScouting.addClimbLevel(builder, this.level);
-    SubmitDataScouting.addComment(builder, comment);
-    builder.finish(SubmitDataScouting.endSubmitDataScouting(builder));
+    SubmitActions.startSubmitActions(builder);
+    SubmitActions.addActionsList(builder, actionsVector);
+    builder.finish(SubmitActions.endSubmitActions(builder));
 
     const buffer = builder.asUint8Array();
-    const res = await fetch('/requests/submit/data_scouting', {
+    const res = await fetch('/requests/submit/actions', {
       method: 'POST',
       body: buffer,
     });
@@ -192,6 +272,7 @@
     if (res.ok) {
       // We successfully submitted the data. Report success.
       this.section = 'Success';
+      this.actionList = [];
     } else {
       const resBuffer = await res.arrayBuffer();
       const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
diff --git a/scouting/www/entry/entry.module.ts b/scouting/www/entry/entry.module.ts
index a2aa7bb..c74d4d0 100644
--- a/scouting/www/entry/entry.module.ts
+++ b/scouting/www/entry/entry.module.ts
@@ -1,22 +1,11 @@
 import {NgModule, Pipe, PipeTransform} from '@angular/core';
 import {CommonModule} from '@angular/common';
 import {FormsModule} from '@angular/forms';
-
-import {CounterButtonModule} from '@org_frc971/scouting/www/counter_button';
 import {EntryComponent} from './entry.component';
 
-import {ClimbLevel} from '../../webserver/requests/messages/submit_data_scouting_generated';
-
-@Pipe({name: 'levelToString'})
-export class LevelToStringPipe implements PipeTransform {
-  transform(level: ClimbLevel): string {
-    return ClimbLevel[level];
-  }
-}
-
 @NgModule({
-  declarations: [EntryComponent, LevelToStringPipe],
+  declarations: [EntryComponent],
   exports: [EntryComponent],
-  imports: [CommonModule, FormsModule, CounterButtonModule],
+  imports: [CommonModule, FormsModule],
 })
 export class EntryModule {}
diff --git a/scouting/www/entry/entry.ng.html b/scouting/www/entry/entry.ng.html
index e73cfb0..ec95f39 100644
--- a/scouting/www/entry/entry.ng.html
+++ b/scouting/www/entry/entry.ng.html
@@ -49,386 +49,133 @@
     <div class="buttons">
       <!-- hack to right align the next button -->
       <div></div>
-      <button class="btn btn-primary" (click)="nextSection()">Next</button>
+      <button class="btn btn-primary" (click)="changeSectionTo('Init');">
+        Next
+      </button>
     </div>
   </div>
 
-  <div *ngSwitchCase="'Auto'" id="auto" class="container-fluid">
-    <button class="buttons" id="switch_field_button" (click)="flipImages()">
-      Flip
-    </button>
-    <div class="row">
-      <img
-        id="field_quadrants_image"
-        src="/sha256/cbb99a057a2504e80af526dae7a0a04121aed84c56a6f4889e9576fe1c20c61e/pictures/field/quadrants.jpeg"
-        alt="Quadrants Image"
-      />
-      <form>
+  <div *ngSwitchCase="'Init'" id="init" class="container-fluid">
+    <h2>Select Starting Position</h2>
+    <div *ngFor="let i of [1, 2, 3, 4]">
+      <label>
         <input
           type="radio"
-          [(ngModel)]="quadrant"
-          name="quadrant"
-          id="quadrant1"
-          [value]="1"
+          name="radio-group"
+          [value]="i"
+          (change)="selectedValue = $event.target.value"
         />
-        <label for="quadrant1">Quadrant 1</label>
-        <input
-          type="radio"
-          [(ngModel)]="quadrant"
-          name="quadrant"
-          id="quadrant2"
-          [value]="2"
-        />
-        <label for="quadrant2">Quadrant 2</label>
-        <br />
-        <input
-          type="radio"
-          [(ngModel)]="quadrant"
-          name="quadrant"
-          id="quadrant3"
-          [value]="3"
-        />
-        <label for="quadrant3">Quadrant 3</label>
-        <input
-          type="radio"
-          [(ngModel)]="quadrant"
-          name="quadrant"
-          id="quadrant4"
-          [value]="4"
-        />
-        <label for="quadrant4">Quadrant 4</label>
-      </form>
-    </div>
-    <div class="row">
-      <img
-        id="field_balls_image"
-        src="/sha256/e095cc8a75d804b0e2070e0a941fab37154176756d4c1a775e53cc48c3a732b9/pictures/field/balls.jpeg"
-        alt="Balls Image"
-      />
-      <form>
-        <!--Choice for each ball location-->
-        <input
-          [(ngModel)]="ball1"
-          type="checkbox"
-          name="1ball"
-          value="1"
-          id="ball-1"
-        />
-        <label for="ball-1">Ball 1</label>
-        <input
-          [(ngModel)]="ball2"
-          type="checkbox"
-          name="2ball"
-          value="2"
-          id="ball-2"
-        />
-        <label for="ball-2">Ball 2</label>
-        <br />
-        <input
-          [(ngModel)]="ball3"
-          type="checkbox"
-          name="3ball"
-          value="3"
-          id="ball-3"
-        />
-        <label for="ball-3">Ball 3</label>
-        <input
-          [(ngModel)]="ball4"
-          type="checkbox"
-          name="4ball"
-          value="4"
-          id="ball-4"
-        />
-        <label for="ball-4">Ball 4</label>
-        <br />
-        <input
-          [(ngModel)]="ball5"
-          type="checkbox"
-          name="5ball"
-          value="5"
-          id="ball-5"
-        />
-        <label for="ball-5">Ball 5</label>
-      </form>
-    </div>
-    <div class="row justify-content-center">
-      <frc971-counter-button class="col-4" [(value)]="autoUpperShotsMade">
-        Upper
-      </frc971-counter-button>
-      <frc971-counter-button class="col-4" [(value)]="autoLowerShotsMade">
-        Lower
-      </frc971-counter-button>
-      <frc971-counter-button class="col-4" [(value)]="autoShotsMissed">
-        Missed
-      </frc971-counter-button>
-    </div>
-    <div class="buttons">
-      <button class="btn btn-primary" (click)="prevSection()">Back</button>
-      <button class="btn btn-primary" (click)="nextSection()">Next</button>
-    </div>
-  </div>
-
-  <div *ngSwitchCase="'TeleOp'" id="teleop" class="container-fluid">
-    <div class="row justify-content-center">
-      <frc971-counter-button class="col-4" [(value)]="teleUpperShotsMade">
-        Upper
-      </frc971-counter-button>
-      <frc971-counter-button class="col-4" [(value)]="teleLowerShotsMade">
-        Lower
-      </frc971-counter-button>
-      <frc971-counter-button class="col-4" [(value)]="teleShotsMissed">
-        Missed
-      </frc971-counter-button>
-    </div>
-    <div class="buttons">
-      <button class="btn btn-primary" (click)="prevSection()">Back</button>
-      <button class="btn btn-primary" (click)="nextSection()">Next</button>
-    </div>
-  </div>
-
-  <div *ngSwitchCase="'Climb'" id="climb" class="container-fluid">
-    <form>
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="no_attempt"
-        [value]="ClimbLevel.NoAttempt"
-      />
-      <label for="no_attempt">No climbing attempt</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="low"
-        [value]="ClimbLevel.Low"
-      />
-      <label for="low">Low</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="medium"
-        [value]="ClimbLevel.Medium"
-      />
-      <label for="medium">Medium</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="high"
-        [value]="ClimbLevel.High"
-      />
-      <label for="high">High</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="traversal"
-        [value]="ClimbLevel.Traversal"
-      />
-      <label for="traversal">Traversal</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="failed"
-        [value]="ClimbLevel.Failed"
-      />
-      <label for="failed">Failed</label>
-      <br />
-      <input
-        [(ngModel)]="level"
-        type="radio"
-        name="level"
-        id="failed_with_plenty_of_time"
-        [value]="ClimbLevel.FailedWithPlentyOfTime"
-      />
-      <label for="failed_with_plenty_of_time">
-        Failed (attempted with more than 10 seconds left)
+        {{ i }}
       </label>
-      <br />
-    </form>
+    </div>
     <div class="buttons">
-      <button class="btn btn-primary" (click)="prevSection()">Back</button>
-      <button class="btn btn-primary" (click)="nextSection()">Next</button>
+      <button
+        class="btn btn-primary"
+        [disabled]="!selectedValue"
+        (click)="changeSectionTo('Pickup'); addAction({type: 'startMatchAction', position: selectedValue});"
+      >
+        Start Match
+      </button>
     </div>
   </div>
 
-  <div *ngSwitchCase="'Other'" id="defense" class="container-fluid">
-    <h4 class="text-center">
-      How much did other robots play defense against this robot?
-    </h4>
-    0 - No defense played against this robot
-    <br />
-    1 - Minimal defense
-    <br />
-    2 - Some defense
-    <br />
-    3 - About half the match was played against defense
-    <br />
-    4 - Good amount of defense
-    <br />
-    5 - Constant defense
-    <div class="row" style="min-height: 50px">
-      <div class="col-10">
-        <input
-          type="range"
-          min="0"
-          max="5"
-          [(ngModel)]="defensePlayedOnScore"
-        />
-      </div>
-      <div class="col">
-        <h6>{{defensePlayedOnScore}}</h6>
-      </div>
-    </div>
-
-    <h4 class="text-center">
-      How much did this robot play defense against other robots?
-    </h4>
-    0 - This robot did not play defense
-    <br />
-    1 - Minimal defense
-    <br />
-    2 - Some defense
-    <br />
-    3 - Defense was played for about half the match
-    <br />
-    4 - Good amount of defense
-    <br />
-    5 - Constant defense
-    <div class="row">
-      <div class="col-10">
-        <input type="range" min="0" max="5" [(ngModel)]="defensePlayedScore" />
-      </div>
-      <div class="col">
-        <h6>{{defensePlayedScore}}</h6>
-      </div>
-    </div>
-
-    <div class="row">
-      <form>
-        <input
-          type="checkbox"
-          [(ngModel)]="noShow"
-          name="no_show"
-          id="no_show"
-        />
-        <label for="no_show">No show</label>
-        <br />
-        <input
-          type="checkbox"
-          [(ngModel)]="neverMoved"
-          name="never_moved"
-          id="never_moved"
-        />
-        <label for="never_moved">Never moved</label>
-        <br />
-        <input
-          type="checkbox"
-          [(ngModel)]="batteryDied"
-          name="battery_died"
-          id="battery_died"
-        />
-        <label for="battery_died">Battery died</label>
-        <br />
-        <input
-          type="checkbox"
-          [(ngModel)]="mechanicallyBroke"
-          name="mechanically_broke"
-          id="mechanically_broke"
-        />
-        <label for="mechanically_broke">Broke (mechanically)</label>
-        <br />
-        <input
-          type="checkbox"
-          [(ngModel)]="lostComs"
-          name="lost_coms"
-          id="lost_coms"
-        />
-        <label for="lost_coms">Lost coms</label>
-      </form>
-    </div>
-
-    <div class="row">
-      <h4>General Comments About Match</h4>
-      <textarea
-        [(ngModel)]="comment"
-        id="comment"
-        placeholder="optional"
-      ></textarea>
-    </div>
-
-    <div class="buttons">
-      <button class="btn btn-primary" (click)="prevSection()">Back</button>
-      <button class="btn btn-primary" (click)="nextSection()">Next</button>
-    </div>
+  <div *ngSwitchCase="'Pickup'" id="PickUp" class="container-fluid">
+    <button class="btn btn-danger" (click)="undoLastAction()">UNDO</button>
+    <button
+      class="btn btn-warning"
+      (click)="changeSectionTo('Place'); addAction({type: 'pickupObjectAction', objectType: ObjectType.kCone});"
+    >
+      CONE
+    </button>
+    <button
+      class="btn btn-primary"
+      (click)="changeSectionTo('Place'); addAction({type: 'pickupObjectAction', objectType: ObjectType.kCube});"
+    >
+      CUBE
+    </button>
+    <button
+      *ngIf="autoPhase"
+      class="btn btn-info"
+      (click)="autoPhase = false; addAction({type: 'endAutoPhase'});"
+    >
+      Start Teleop
+    </button>
+    <button
+      *ngIf="!autoPhase"
+      class="btn btn-info"
+      (click)="changeSectionTo('Endgame')"
+    >
+      Endgame
+    </button>
   </div>
 
-  <div *ngSwitchCase="'Review and Submit'" id="review" class="container-fluid">
-    <h4>Team Selection</h4>
-    <ul>
-      <li>Match number: {{matchNumber}}</li>
-      <li>Team number: {{teamNumber}}</li>
-      <li>SetNumber: {{setNumber}}</li>
-      <li>Comp Level: {{COMP_LEVEL_LABELS[compLevel]}}</li>
-    </ul>
-
-    <h4>Auto</h4>
-    <ul>
-      <li>Quadrant: {{quadrant}}</li>
-      <li>Collected Ball 1: {{ball1}}</li>
-      <li>Collected Ball 2: {{ball2}}</li>
-      <li>Collected Ball 3: {{ball3}}</li>
-      <li>Collected Ball 4: {{ball4}}</li>
-      <li>Collected Ball 5: {{ball5}}</li>
-      <li>Upper Shots Made: {{autoUpperShotsMade}}</li>
-      <li>Lower Shots Made: {{autoLowerShotsMade}}</li>
-      <li>Missed Shots: {{autoShotsMissed}}</li>
-    </ul>
-
-    <h4>TeleOp</h4>
-    <ul>
-      <li>Upper Shots Made: {{teleUpperShotsMade}}</li>
-      <li>Lower Shots Made: {{teleLowerShotsMade}}</li>
-      <li>Missed Shots: {{teleShotsMissed}}</li>
-    </ul>
-
-    <h4>Climb</h4>
-    <ul>
-      <li>Climb Level: {{level | levelToString}}</li>
-    </ul>
-
-    <h4>Other</h4>
-    <ul>
-      <li>Defense Played On Rating: {{defensePlayedOnScore}}</li>
-      <li>Defense Played Rating: {{defensePlayedScore}}</li>
-      <li>No show: {{noShow}}</li>
-      <li>Never moved: {{neverMoved}}</li>
-      <li>Battery died: {{batteryDied}}</li>
-      <li>Broke (mechanically): {{mechanicallyBroke}}</li>
-      <li>Lost coms: {{lostComs}}</li>
-      <li>Comments: {{comment}}</li>
-    </ul>
-
-    <span class="error_message">{{ errorMessage }}</span>
-
-    <div class="buttons">
-      <button class="btn btn-primary" (click)="prevSection()">Back</button>
-      <button class="btn btn-primary" (click)="nextSection()">Submit</button>
-    </div>
+  <div *ngSwitchCase="'Place'" id="Place" class="container-fluid">
+    <button class="btn btn-danger" (click)="undoLastAction()">UNDO</button>
+    <button
+      class="btn btn-success"
+      (click)="changeSectionTo('Pickup'); addAction({type: 'placeObjectAction', scoreLevel: ScoreLevel.kHigh});"
+    >
+      HIGH
+    </button>
+    <button
+      class="btn btn-warning"
+      (click)="changeSectionTo('Pickup'); addAction({type: 'placeObjectAction', scoreLevel: ScoreLevel.kMiddle});"
+    >
+      MID
+    </button>
+    <button
+      class="btn btn-danger"
+      (click)="changeSectionTo('Pickup'); addAction({type: 'placeObjectAction', scoreLevel: ScoreLevel.kLow});"
+    >
+      LOW
+    </button>
+    <button
+      *ngIf="autoPhase"
+      class="btn btn-info"
+      (click)="autoPhase = false; addAction({type: 'endAutoPhase'});"
+    >
+      Start Teleop
+    </button>
+    <button
+      *ngIf="!autoPhase"
+      class="btn btn-info"
+      (click)="changeSectionTo('Endgame')"
+    >
+      Endgame
+    </button>
   </div>
 
-  <div *ngSwitchCase="'Success'" id="success" class="container-fluid">
-    <span>Successfully submitted scouting data.</span>
-    <div class="buttons justify-content-end">
-      <button class="btn btn-primary" (click)="nextSection()">Continue</button>
-    </div>
+  <div *ngSwitchCase="'Endgame'" id="Endgame" class="container-fluid">
+    <button class="btn btn-danger" (click)="undoLastAction()">UNDO</button>
+    <label>
+      <input type="checkbox" (change)="dockedValue = $event.target.value" />
+      Docked
+    </label>
+    <label>
+      <input type="checkbox" (change)="engagedValue = $event.target.value" />
+      Engaged
+    </label>
+    <button
+      *ngIf="!autoPhase"
+      class="btn btn-info"
+      (click)="changeSectionTo('Review and Submit'); addAction({type: 'endMatchAction', docked: dockedValue, engaged: engagedValue});"
+    >
+      End Match
+    </button>
+  </div>
+
+  <div *ngSwitchCase="'Review and Submit'" id="Review" class="container-fluid">
+    <button class="btn btn-danger" (click)="undoLastAction()">UNDO</button>
+    <button
+      *ngIf="!autoPhase"
+      class="btn btn-warning"
+      (click)="submitActions();"
+    >
+      Submit
+    </button>
+  </div>
+
+  <div *ngSwitchCase="'Success'" id="Success" class="container-fluid">
+    <h2>Successfully submitted data.</h2>
   </div>
 </ng-container>