Scouting App: Add ability to submit data with QR Codes

This patch adds the option for a scout to generate a QR Code
storing the collected data instead of submitting it. Next,
another user can submit the data by scanning the QR Code using the
new 'Scan' tab.

Since flatbuffers are pretty inefficient in terms of space usage, we
have trouble fitting all the data into a single QR code. The app
allows the user to split the data into multiple QR codes which have to
be scanned.

I tried a bunch of upstream QR code scanning libraries, but they're
all either unmaintained, not open source, or just don't work well. I
ended up settling on OpenCV because it was the most reliable library I
could find.

Co-Authored-By: Filip Kujawa <filip.j.kujawa@gmail.com>
Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Signed-off-by: Philipp Schrader <philipp.schrader+971@gmail.com>
Change-Id: I794b54bf7e8389200aa2abe8d05f622a987bca9c
diff --git a/scouting/www/app/app.module.ts b/scouting/www/app/app.module.ts
index bf393e4..ccc4840 100644
--- a/scouting/www/app/app.module.ts
+++ b/scouting/www/app/app.module.ts
@@ -10,6 +10,7 @@
 import {ViewModule} from '../view';
 import {DriverRankingModule} from '../driver_ranking';
 import {PitScoutingModule} from '../pit_scouting';
+import {ScanModule} from '../scan';
 
 @NgModule({
   declarations: [App],
@@ -23,6 +24,7 @@
     DriverRankingModule,
     ViewModule,
     PitScoutingModule,
+    ScanModule,
   ],
   exports: [App],
   bootstrap: [App],
diff --git a/scouting/www/app/app.ng.html b/scouting/www/app/app.ng.html
index 5f7ea9f..ef37c07 100644
--- a/scouting/www/app/app.ng.html
+++ b/scouting/www/app/app.ng.html
@@ -73,6 +73,15 @@
       Pit
     </a>
   </li>
+  <li class="nav-item">
+    <a
+      class="nav-link"
+      [class.active]="tabIs('Scan')"
+      (click)="switchTabToGuarded('Scan')"
+    >
+      Scan
+    </a>
+  </li>
 </ul>
 
 <ng-container [ngSwitch]="tab">
@@ -93,4 +102,5 @@
   <shift-schedule *ngSwitchCase="'ShiftSchedule'"></shift-schedule>
   <app-view *ngSwitchCase="'View'"></app-view>
   <app-pit-scouting *ngSwitchCase="'Pit'"></app-pit-scouting>
+  <app-scan *ngSwitchCase="'Scan'"></app-scan>
 </ng-container>
diff --git a/scouting/www/app/app.ts b/scouting/www/app/app.ts
index 597e5c5..895336b 100644
--- a/scouting/www/app/app.ts
+++ b/scouting/www/app/app.ts
@@ -7,10 +7,11 @@
   | 'DriverRanking'
   | 'ShiftSchedule'
   | 'View'
-  | 'Pit';
+  | 'Pit'
+  | 'Scan';
 
 // Ignore the guard for tabs that don't require the user to enter any data.
-const unguardedTabs: Tab[] = ['MatchList', 'View'];
+const unguardedTabs: Tab[] = ['MatchList', 'Scan', 'View'];
 
 type TeamInMatch = {
   teamNumber: string;