Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/raceliciouss/YUSEN-LIMO-WAREHOUSE/llms.txt

Use this file to discover all available pages before exploring further.

The QR scanning module lets warehouse operators point their device camera at a printed QR code to instantly populate an inbound or outbound shipment form — eliminating manual data entry for common fields like HAWB, MAWB, client name, and plate number. On the Inventory page, the same scanner doubles as a fast search tool, using the decoded value as a live filter term instead of filling a form.

Triggering a Scan

The scan flow starts from a button click in one of two contexts:
  • Inbound / Outbound page — click the Scan button (.scan-btn or .inline-scan-btn) inside the active inbound or outbound tab. The scanner decodes the QR payload and calls populateShipmentForm() to write each field value directly into the open form.
  • Inventory page — click the QR scan button. After a successful decode, the result is passed to applyScannedPayloadToPage(), which writes the decoded HAWB, MAWB, or plain-text value into #inventorySearchInput and immediately re-runs the inventory filter.
In both cases, clicking the trigger opens the #scanModal dialog, which contains a <video id="cameraPreview"> element that displays the live camera stream while the scanner is active.

Camera API Integration

startQrScanner(modal, context) orchestrates the full camera-to-decode pipeline. It is called automatically when a scan trigger is clicked.
1

Request camera access

The browser calls navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } }, audio: false }). The environment facing mode prefers the rear camera on mobile devices, which is more practical for scanning printed labels.
2

Render the live stream

The returned MediaStream is assigned to video.srcObject and played immediately. The live feed appears in the #scanModal preview element (<video id="cameraPreview">), giving the operator real-time visual feedback while they position the code.
3

Capture frames via canvas

On each animation frame (driven by requestAnimationFrame), a temporary off-screen <canvas> element is sized to match the current video dimensions. context2d.drawImage(video, 0, 0, canvas.width, canvas.height) copies the current frame into the canvas, and context2d.getImageData() extracts the raw RGBA pixel buffer.
4

Decode with jsQR

The pixel buffer is passed to window.jsQR(imageData.data, imageData.width, imageData.height). jsQR scans the buffer for a QR pattern and returns an object containing a data string when a valid code is found, or null when the frame contains no readable code. Frames that return null are discarded and the loop continues.
5

Pass the decoded text to handleScannedPayload()

When jsQR returns a non-null result, result.data (the raw text string embedded in the QR code) is immediately forwarded to handleScannedPayload(text, modal, context). The scan loop is halted at this point — the app does not continue scanning after a successful read.
6

Normalize field aliases with normalizeShipmentPayload()

handleScannedPayload() attempts JSON.parse(text). If parsing succeeds, the resulting object is passed to normalizeShipmentPayload(payload), which maps every recognized property alias (e.g., HAWB, hawbNumber, shipmentId) to a single canonical key (e.g., hawb). The function always returns a complete, predictable object regardless of which alias names were used in the source QR code.
7

Write values to the form with populateShipmentForm()

The normalized payload is handed to populateShipmentForm(payload, context). This function queries the active form context for every element that carries a data-field attribute and sets its value to the matching key from the normalized payload. <select> elements are handled by applySelectValue(), which performs case-insensitive, whitespace-tolerant option matching. Standard <input> elements receive .value assignment followed by a synthetic input event so any reactive listeners update correctly. If a stored shipment matching the scanned HAWB or MAWB is found in Local Storage, location and unit are also back-filled from that record.
8

Stop the camera and close the modal

Once the form has been populated, stopQrScanner() stops all active MediaStreamTrack objects from cameraStream, clears the requestAnimationFrame loop, and removes the active class from the camera box. The #scanModal is hidden and its inbound/outbound context classes are removed.

Camera Permission Handling

startQrScanner() wraps the getUserMedia call in a try/catch block. Two distinct failure modes are handled gracefully:
  • Device or browser not supported — if navigator.mediaDevices or getUserMedia is not present on the window object, the function writes "Camera not supported on this device or browser." into the .camera-fallback element inside #scanModal and returns early without throwing.
  • Permission denied or hardware error — if the getUserMedia promise rejects (e.g., the user clicks “Block” in the browser permission dialog, or the camera is in use by another application), the catch block writes "Unable to access camera. Please allow camera permission or use a compatible device." into .camera-fallback and logs the error to the console. The modal remains open so the operator can read the message.
Camera scanning requires a secure context. The getUserMedia API is restricted by browsers to pages served over https:// or from localhost. Opening the app via the file:// protocol — for example, by double-clicking index.html in a file manager — will cause navigator.mediaDevices to be undefined, and the scanner will fall back to the unsupported-device message. Always serve the project through a local web server (e.g., python3 -m http.server 8080) or a TLS-enabled host.
For details on what data to encode in a QR code — including the full list of accepted JSON field aliases and a minimal plain-text fallback format — see QR Payload Format.

Build docs developers (and LLMs) love