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/entry/entry.ng.html b/scouting/www/entry/entry.ng.html
index 22d2d27..139268f 100644
--- a/scouting/www/entry/entry.ng.html
+++ b/scouting/www/entry/entry.ng.html
@@ -476,6 +476,9 @@
     </div>
     <div class="d-grid gap-5">
       <button class="btn btn-secondary" (click)="undoLastAction()">UNDO</button>
+      <button class="btn btn-info" (click)="changeSectionTo('QR Code');">
+        Create QR Code
+      </button>
       <button class="btn btn-warning" (click)="submit2024Actions();">
         Submit
       </button>
@@ -484,6 +487,75 @@
   <div *ngSwitchCase="'Success'" id="Success" class="container-fluid">
     <h2>Successfully submitted data.</h2>
   </div>
+  <div *ngSwitchCase="'QR Code'" id="QR Code" class="container-fluid">
+    <span>Density:</span>
+    <select
+      [(ngModel)]="qrCodeValuePieceSize"
+      (ngModelChange)="updateQrCodeValuePieceSize()"
+      type="number"
+      id="qr_code_piece_size"
+    >
+      <option
+        *ngFor="let pieceSize of QR_CODE_PIECE_SIZES"
+        [ngValue]="pieceSize"
+      >
+        {{pieceSize}}
+      </option>
+    </select>
+    <div class="qr-container">
+      <qrcode
+        [qrdata]="qrCodeValuePieces[qrCodeValueIndex]"
+        [width]="1000"
+        [errorCorrectionLevel]="'M'"
+        [margin]="6"
+        class="qrcode"
+      ></qrcode>
+    </div>
+    <nav class="qrcode-nav">
+      <ul
+        class="qrcode-buttons pagination pagination-lg justify-content-center"
+      >
+        <li class="page-item">
+          <a
+            class="page-link"
+            href="#"
+            aria-label="Previous"
+            (click)="setQrCodeValueIndex(qrCodeValueIndex - 1)"
+          >
+            <span aria-hidden="true">&laquo;</span>
+            <span class="visually-hidden">Previous</span>
+          </a>
+        </li>
+        <li *ngFor="let _ of qrCodeValuePieces; index as i" class="page-item">
+          <a
+            class="page-link"
+            href="#"
+            (click)="setQrCodeValueIndex(i)"
+            [class.active]="qrCodeValueIndex == i"
+          >
+            {{i + 1}}
+          </a>
+        </li>
+        <li class="page-item">
+          <a
+            class="page-link"
+            href="#"
+            aria-label="Next"
+            (click)="setQrCodeValueIndex(qrCodeValueIndex + 1)"
+          >
+            <span aria-hidden="true">&raquo;</span>
+            <span class="visually-hidden">Next</span>
+          </a>
+        </li>
+      </ul>
+    </nav>
+    <button
+      class="btn btn-secondary"
+      (click)="changeSectionTo('Review and Submit')"
+    >
+      BACK
+    </button>
+  </div>
 
   <span class="progress_message" role="alert">{{ progressMessage }}</span>
   <span class="error_message" role="alert">{{ errorMessage }}</span>