Add delete functionality to the scouting app

This patch adds the ability to delete data scouting entries
from the database through the view tab.

Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: I294fd3ebfa3721dace00582ba7f22e3da5f0f419
diff --git a/scouting/www/view/BUILD b/scouting/www/view/BUILD
index d787e8f..67c7e3b 100644
--- a/scouting/www/view/BUILD
+++ b/scouting/www/view/BUILD
@@ -10,6 +10,8 @@
     ],
     deps = [
         ":node_modules/@angular/forms",
+        "//scouting/webserver/requests/messages:delete_2023_data_scouting_response_ts_fbs",
+        "//scouting/webserver/requests/messages:delete_2023_data_scouting_ts_fbs",
         "//scouting/webserver/requests/messages:error_response_ts_fbs",
         "//scouting/webserver/requests/messages:request_2023_data_scouting_response_ts_fbs",
         "//scouting/webserver/requests/messages:request_2023_data_scouting_ts_fbs",
diff --git a/scouting/www/view/view.component.ts b/scouting/www/view/view.component.ts
index 561ae08..c75f83b 100644
--- a/scouting/www/view/view.component.ts
+++ b/scouting/www/view/view.component.ts
@@ -1,4 +1,6 @@
 import {Component, OnInit} from '@angular/core';
+import {Builder, ByteBuffer} from 'flatbuffers';
+import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
 import {
   Ranking,
   RequestAllDriverRankingsResponse,
@@ -11,6 +13,8 @@
   Note,
   RequestAllNotesResponse,
 } from '../../webserver/requests/messages/request_all_notes_response_generated';
+import {Delete2023DataScouting} from '../../webserver/requests/messages/delete_2023_data_scouting_generated';
+import {Delete2023DataScoutingResponse} from '../../webserver/requests/messages/delete_2023_data_scouting_response_generated';
 
 import {ViewDataRequestor} from '../rpc';
 
@@ -104,16 +108,83 @@
   }
 
   // TODO(Filip): Add delete functionality.
-  // Gets called when a user clicks the delete icon.
-  async deleteData() {
+  // Gets called when a user clicks the delete icon (note scouting).
+  async deleteNoteData() {
     const block_alerts = document.getElementById(
       'block_alerts'
     ) as HTMLInputElement;
-    if (!block_alerts.checked) {
-      if (!window.confirm('Actually delete data?')) {
-        this.errorMessage = 'Deleting data has not been implemented yet.';
-        return;
-      }
+    if (block_alerts.checked || window.confirm('Actually delete data?')) {
+      this.errorMessage = 'Deleting data has not been implemented yet.';
+      return;
+    }
+  }
+
+  // TODO(Filip): Add delete functionality.
+  // Gets called when a user clicks the delete icon (driver ranking).
+  async deleteDriverRankingData() {
+    const block_alerts = document.getElementById(
+      'block_alerts'
+    ) as HTMLInputElement;
+    if (block_alerts.checked || window.confirm('Actually delete data?')) {
+      this.errorMessage = 'Deleting data has not been implemented yet.';
+      return;
+    }
+  }
+
+  // Gets called when a user clicks the delete icon.
+  async deleteDataScouting(
+    compLevel: string,
+    matchNumber: number,
+    setNumber: number,
+    teamNumber: string
+  ) {
+    const block_alerts = document.getElementById(
+      'block_alerts'
+    ) as HTMLInputElement;
+    if (block_alerts.checked || window.confirm('Actually delete data?')) {
+      await this.requestDeleteDataScouting(
+        compLevel,
+        matchNumber,
+        setNumber,
+        teamNumber
+      );
+      await this.fetchStats2023();
+    }
+  }
+
+  async requestDeleteDataScouting(
+    compLevel: string,
+    matchNumber: number,
+    setNumber: number,
+    teamNumber: string
+  ) {
+    this.progressMessage = 'Deleting data. Please be patient.';
+    const builder = new Builder();
+    const compLevelData = builder.createString(compLevel);
+    const teamNumberData = builder.createString(teamNumber);
+
+    builder.finish(
+      Delete2023DataScouting.createDelete2023DataScouting(
+        builder,
+        compLevelData,
+        matchNumber,
+        setNumber,
+        teamNumberData
+      )
+    );
+
+    const buffer = builder.asUint8Array();
+    const res = await fetch('/requests/delete/delete_2023_data_scouting', {
+      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}"`;
     }
   }
 
diff --git a/scouting/www/view/view.ng.html b/scouting/www/view/view.ng.html
index 667765f..2273217 100644
--- a/scouting/www/view/view.ng.html
+++ b/scouting/www/view/view.ng.html
@@ -11,12 +11,22 @@
   </button>
   <ul class="dropdown-menu">
     <li>
-      <a class="dropdown-item" href="#" (click)="switchDataSource('Notes')">
+      <a
+        class="dropdown-item"
+        href="#"
+        (click)="switchDataSource('Notes')"
+        id="notes_source_dropdown"
+      >
         Notes
       </a>
     </li>
     <li>
-      <a class="dropdown-item" href="#" (click)="switchDataSource('Stats2023')">
+      <a
+        class="dropdown-item"
+        href="#"
+        (click)="switchDataSource('Stats2023')"
+        id="stats_source_dropdown"
+      >
         Stats
       </a>
     </li>
@@ -25,6 +35,7 @@
         class="dropdown-item"
         href="#"
         (click)="switchDataSource('DriverRanking')"
+        id="driver_ranking_source_dropdown"
       >
         Driver Ranking
       </a>
@@ -64,7 +75,7 @@
           <td>{{parseKeywords(note)}}</td>
           <!-- Delete Icon. -->
           <td>
-            <button class="btn btn-danger" (click)="deleteData()">
+            <button class="btn btn-danger" (click)="deleteNoteData()">
               <i class="bi bi-trash"></i>
             </button>
           </td>
@@ -94,13 +105,17 @@
       </thead>
       <tbody>
         <tr *ngFor="let stat2023 of statList; index as i;">
-          <th scope="row">{{stat2023.match()}}</th>
-          <td>{{stat2023.team()}}</td>
+          <th scope="row">{{stat2023.matchNumber()}}</th>
+          <td>{{stat2023.teamNumber()}}</td>
           <td>{{stat2023.setNumber()}}</td>
           <td>{{COMP_LEVEL_LABELS[stat2023.compLevel()]}}</td>
           <!-- Delete Icon. -->
           <td>
-            <button class="btn btn-danger" (click)="deleteData()">
+            <button
+              class="btn btn-danger"
+              id="delete_button_{{i}}"
+              (click)="deleteDataScouting(stat2023.compLevel(), stat2023.matchNumber(), stat2023.setNumber(), stat2023.teamNumber())"
+            >
               <i class="bi bi-trash"></i>
             </button>
           </td>
@@ -136,7 +151,7 @@
           <td>{{ranking.rank3()}}</td>
           <!-- Delete Icon. -->
           <td>
-            <button class="btn btn-danger" (click)="deleteData()">
+            <button class="btn btn-danger" (click)="deleteDriverRankingData()">
               <i class="bi bi-trash"></i>
             </button>
           </td>