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.

Raw shipment records in warehouseShipments represent individual transaction events — one record per inbound receipt, one per outbound release. Nothing is pre-computed at write-time. Every inventory row, every dashboard statistic, and every activity entry is produced by reading the flat array and running a series of in-memory transformations at render-time. The central function driving all of this is aggregateShipmentsByReference() in assets/app.js, which two derived accessors — getInventoryData() and getActivityData() — both call on top of getStoredShipments().

aggregateShipmentsByReference()

aggregateShipmentsByReference(shipments) accepts the raw shipment array returned by getStoredShipments() and returns one aggregate object per unique cargo reference. A reference is identified by getShipmentAggregateKey(), which resolves in priority order:
  1. HAWB — if shipment.hawb is a non-empty string (after trimming and upper-casing), the key is hawb:<HAWB>.
  2. MAWB — if hawb is absent but shipment.mawb is present, the key is mawb:<MAWB>.
  3. Timestamp fallback — if neither is set, the key is manual:<savedAt>, meaning the record is treated as a standalone entry and never merged.
The function iterates every record and, for each key, maintains a running aggregate object in a Map. The merge rules are:
  • Latest record wins for metadata — descriptive fields (client, destination, location, status, transactionType, trucker, driver, etc.) are overwritten only when the incoming record’s savedAt is equal to or greater than the aggregate’s current savedAt. This means the most recently saved record’s field values win.
  • Inbound records accumulate qtyInValuegetShipmentEntryType() inspects the entryType field (falling back to presence of qtyOut, releaseQty, releaseDate, releasePlate, or releasedBy if entryType is absent). For each inbound record, parseQuantityNumber(shipment.qtyIn || shipment.quantity) is added to the aggregate’s qtyInValue counter.
  • Outbound records accumulate qtyOutValue and outboundDetails — for each outbound record, parseQuantityNumber(shipment.qtyOut || shipment.releaseQty || shipment.quantity) is added to qtyOutValue, and a detail object { date, time, qty, plate, driver, remarks, savedAt } is pushed to outboundDetails.
After all records are processed, outboundDetails for each aggregate is sorted chronologically by parsing detail.date + "T" + (detail.time || "00:00:00") into a timestamp. The latest entry in the sorted array is then promoted to the top-level release summary fields (releaseDate, releaseTime, releasePlate, releaseDriver, remarks). Finally, remainingValue is computed:
remainingValue = qtyInValue - qtyOutValue
remainingValue is the authoritative remaining-cargo figure used throughout the UI. It is exposed on inventory rows as the formatted string remainingQuantity (via normalizeShipmentForInventory()).

Partial Outbound Handling

A single HAWB can have cargo released across multiple separate transactions. Each outbound form submission produces a new record in warehouseShipments with entryType: "outbound" and its own qtyOut value. aggregateShipmentsByReference() handles this by:
  1. Summing all outbound quantities — every outbound record for the same reference adds to qtyOutValue independently, so three partial releases of 10 boxes each produce a qtyOutValue of 30.
  2. Preserving the full outbound history — each release event is appended to outboundDetails in the order it is encountered, then the entire array is sorted chronologically.
  3. Surfacing the most recent release — after sorting, the function finds the latest detail that has a date value and copies its fields to the aggregate’s top-level releaseDate, releaseTime, releasePlate, releaseDriver, and remarks. This means the inventory and activity views always show the most recent release event in the summary row, while the full outboundDetails array is available in the drawer.

Dashboard Statistics

The dashboard calls aggregateShipmentsByReference(getStoredShipments()) and derives three headline numbers from the result:
StatisticElement IDCalculation
Total ShipmentsdashboardTotalShipmentsshipmentRows.length — count of unique aggregated references
Cargo in WarehousedashboardCargoInWarehouseshipmentRows.reduce((sum, s) => sum + (s.remainingValue || 0), 0) — sum of remainingValue across all aggregates
Outgoing TodaydashboardOutgoingTodayCount of aggregates where itemHasTodayOutbound(shipment, today) returns true — i.e. any outbound detail whose date matches today’s ISO date string
Each headline card is also a filter button. Clicking Total Shipments resets the dashboard activity list to all. Clicking Cargo in Warehouse filters to aggregates with remainingValue > 0. Clicking Outgoing Today filters to aggregates with an outbound event dated today. The active card is tracked in the local variable dashboardActivityFilter.

Search Algorithm

Global search is initiated from the topbar input present on every authenticated page. The matching logic compares the lowercased query string against three fields on each aggregated shipment:
[item.hawb || '', item.mawb || '', item.client || ''].some(
  (field) => String(field).toLowerCase().includes(query)
)
Matches are grouped into two suggestion categories — Inventory and Activity Report — and displayed in a dropdown (capped at 10 results per group). Selecting a suggestion:
  1. Writes a JSON object to sessionStorage under the key warehouseLiveSearchSelection:
    { "targetPage": "inventory", "value": "HAWB-20240601-001" }
    
  2. Navigates to inventory.html (or dashboard.html for Activity Report suggestions).
On the destination page, the code reads warehouseLiveSearchSelection from sessionStorage, pre-fills the search input with the stored value, triggers a filtered render, and then removes the key with sessionStorage.removeItem('warehouseLiveSearchSelection') so the selection does not persist beyond the initial load.

Filters

Inventory Filters

The inventory filter panel exposes three controls that are applied together before rendering the table:
FilterSourceBehavior
LocationinventoryLocation selectExact match against item.location
Transaction typeinventoryTransaction selectExact match against item.transactionType
Search queryText inputLowercase substring match against HAWB, MAWB, or client
Clicking Clear Filters resets all three controls and re-renders the table unfiltered.

Activity Filters

The activity filter panel adds date-range controls to the same set:
FilterBehavior
Date from / Date toFilters to aggregates whose dateIn or dateOut falls within the range
Transaction typeExact match against transactionType
LocationExact match against location
Search queryLowercase substring match against HAWB, MAWB, or client

Dashboard Activity List Filters

As described in Dashboard Statistics, clicking a dashboard card applies a filter to the activity list rendered below the headline cards:
  • all — all aggregates
  • remaining — aggregates where remainingValue > 0
  • outgoingToday — aggregates with an outbound event dated today

Sorting

Inventory Table

The inventory table exposes a single sort control (inventory-sort select element). The sort is applied to the remainingQuantity string after parsing it back to a number:
OptionBehavior
DefaultSorted by savedAt descending (most recent first)
ascAscending by remaining quantity (lowest first)
descDescending by remaining quantity (highest first)
The sort is re-applied on every filter change via the refreshInventory callback, which is also bound to the change event on the sort select.

User Management Table

The user table supports multi-column sorting. The sortable column indices map to keys as follows:
Column indexSort key
0employeeId
1name
2userId (username)
3role
4status
5lastLogin
Sort state is tracked in userManagementSortState ({ key, direction }). Clicking the same column header again toggles direction between asc and desc. All comparisons are performed after calling .toLowerCase() on the field value, making sorting case-insensitive.

Build docs developers (and LLMs) love