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 YUSEN LIMO Warehouse System is a single-page-style static web application. There is no build step, no module bundler, and no server-side runtime. All application logic is written in vanilla JavaScript and loaded directly by HTML <script> tags. State is persisted entirely in the browser’s Local Storage, and the app is fully functional from any static file server. Understanding how the files relate to each other — and how data flows between them — is the foundation for maintaining or extending the codebase.

Folder Structure

/ (root)
  index.html
  README.md
  styles.css
  assets/
    app.css
    app.js
    auth.js
    login.js
    login_bg.png
    logo-YL.png
    logo.svg
  pages/
    dashboard.html
    inbound-outbound.html
    inventory.html
    profile.html
    user-management.html
  tests/
    auth.test.js

File Responsibilities

Each file has a clearly scoped role. The table below maps files to their purpose, extracted from the maintenance notes in the developer documentation.
FilePurpose
index.htmlLogin page and application entry point. Loads styles.css, assets/auth.js, and assets/login.js.
styles.cssLogin screen styles, branding layout, and background image placement.
assets/auth.jsAuthService object: authentication, session lifecycle, user account CRUD, and Local Storage persistence for accounts and sessions.
assets/login.jsLogin form behavior: reads form inputs, calls AuthService.authenticateUser(), handles errors, and redirects on success.
assets/app.jsMain application logic for all authenticated pages: page guard, data aggregation, inventory/activity rendering, QR scanning, notifications, profile management, and export.
assets/app.cssShared authenticated UI styles: sidebar, topbar, cards, tables, modals, drawer, filters, and responsive breakpoints.
pages/dashboard.htmlDashboard cards (total shipments, cargo in warehouse, outgoing today) and activity report interface.
pages/inbound-outbound.htmlTabbed inbound and outbound shipment entry forms with QR scan support and quantity row management.
pages/inventory.htmlInventory table with search, filters, sort, pagination, and a slide-out detail drawer.
pages/profile.htmlUser profile metadata editor and profile picture upload modal.
pages/user-management.htmlAdmin-only user table with add, edit, reset password, status toggle, and delete actions.
tests/auth.test.jsNode.js unit tests for AuthService logic. Run with node tests/auth.test.js.

Login Flow

The login sequence is the entry point for every user session. It spans index.html, assets/auth.js, and assets/login.js.
1

Browser opens index.html

The browser loads index.html, which includes assets/auth.js and assets/login.js as synchronous <script> tags. AuthService.initializeAuth() is called immediately when auth.js is parsed, seeding default accounts into warehouseAuthAccounts in Local Storage if none exist.
2

login.js checks AuthService.isAuthenticated()

Inside the DOMContentLoaded handler in login.js, after wiring up the password-visibility toggle, the script checks for an existing session:
if (window.AuthService?.isAuthenticated?.()) {
  window.location.href = 'pages/dashboard.html';
  return;
}
isAuthenticated() calls the internal readSession() helper, which reads and parses warehouseAuthSession from Local Storage. If a valid session object with a userId field is found, the function returns true.
3

If authenticated → redirect to pages/dashboard.html

A returning user with an active session is immediately redirected to pages/dashboard.html without ever seeing the login form. The redirect is a standard window.location.href assignment.
4

On form submit → AuthService.authenticateUser() validates credentials

When the user submits the login form, login.js calls:
const result = window.AuthService?.authenticateUser?.(userId, password);
Inside authenticateUser(), credentials are trimmed, then matched against the account list returned by readAccounts(). The comparison is a strict string equality check against the stored password field — there is no hashing at this stage.
5

Success → persist session under warehouseAuthSession, redirect to dashboard

On a successful credential match, authenticateUser() constructs a session object:
const session = {
  userId: account.userId,
  role: account.role,
  name: account.name,
  employeeId: account.employeeId,
  position: account.position
};
persistSession(session);
persistSession() serializes the object to warehouseAuthSession in Local Storage. The lastLogin timestamp is also updated on the matching account in warehouseAuthAccounts. login.js then redirects to pages/dashboard.html.
6

Failure → display error message on login page

If the user ID is not found or the password does not match, authenticateUser() returns { success: false, message: '...' }. The login.js handler writes this message to the #loginError element using setError() and re-enables the submit button via setLoading(false).
Every page inside pages/ loads assets/app.js, which runs guardPageAccess() on startup:
const guardPageAccess = () => {
  if (typeof AuthService === 'undefined' || !AuthService.isAuthenticated?.()) {
    if (isProtectedPage) {
      window.location.replace('../index.html');
    }
    return false;
  }

  if (isAdminRoute && AuthService.getCurrentUser?.()?.role !== 'admin') {
    window.location.replace('dashboard.html');
    return false;
  }

  return true;
};
isProtectedPage is true whenever the path includes /pages/. isAdminRoute matches paths containing /admin, /settings, or user-management. Any unauthenticated user is bounced back to index.html; any non-admin attempting to reach an admin route is redirected to dashboard.html. The sidebar and topbar are shared across all authenticated HTML pages. Admin-only navigation items (the User Management link) use the CSS class .admin-only-nav. app.js reads the current role from AuthService.getCurrentUser() and hides or shows these elements at page initialization.

Data Flow

Shipment data originates from the inbound/outbound forms and flows through a multi-stage pipeline before it is displayed in the inventory or activity views.
User saves a shipment form


saveStoredShipments(shipments)
  ├── try: localStorage.setItem(STORAGE_KEY, ...)   // persist to 'warehouseShipments'
  │         catch: inMemoryShipmentStore = shipments  // fallback if localStorage fails
  └── window.dispatchEvent(new Event('warehouse:data-updated'))


        Event handler in app.js
  ├── activityData = getActivityData()
  │     └── aggregateShipmentsByReference(getStoredShipments())
  │           .map(normalizeShipmentForActivity)
  ├── inventoryData = getInventoryData()
  │     └── aggregateShipmentsByReference(getStoredShipments())
  │           .map(normalizeShipmentForInventory)
  ├── refreshInventory()     // re-renders inventory table
  └── refreshActivity()      // re-renders activity table
getStoredShipments() reads from localStorage.getItem(STORAGE_KEY) (key: warehouseShipments) and falls back to inMemoryShipmentStore if Local Storage is unavailable. The central aggregation function aggregateShipmentsByReference() groups raw shipment records by HAWB or MAWB, computes qtyInValue and qtyOutValue totals, derives remainingQuantity (qtyInValue - qtyOutValue), and collects all outbound events into outboundDetails sorted chronologically. Both the inventory page and the activity page consume the output of this function — they differ only in how normalizeShipmentForInventory and normalizeShipmentForActivity reshape each aggregated record for their respective table columns.

Global Event System

The warehouse:data-updated custom event is the application’s internal data bus. It decouples the shipment save operation from the UI refresh cycle. Any code that modifies shipment data calls saveStoredShipments(), which always dispatches the event at the end:
window.dispatchEvent(new Event('warehouse:data-updated'));
A single listener registered in app.js handles all downstream refreshes:
window.addEventListener('warehouse:data-updated', () => {
  renderDashboardData();
  activityData = getActivityData();
  inventoryData = getInventoryData();
  refreshNotificationState();
  updateNotificationBadge();
  renderNotificationPanel();
  if (typeof refreshInventory === 'function') {
    refreshInventory();
  }
  if (typeof refreshActivity === 'function') {
    refreshActivity();
  }
});
refreshInventory and refreshActivity are module-level variables that are assigned to the page-specific render functions (renderInventoryPage and renderActivityPage) only after those pages have been initialized. The typeof guard prevents errors on pages where those functions are not yet defined.

Key Global Variables

The following variables are declared at the top of assets/app.js and are accessible throughout the file. They represent the shared application state for any authenticated page.
VariableTypePurpose
STORAGE_KEYstringLocal Storage key for all shipment records. Value: 'warehouseShipments'.
refreshInventoryfunction | nullAssigned to renderInventoryPage(1) after the inventory page initializes. Called by the warehouse:data-updated handler to re-render the inventory table.
refreshActivityfunction | nullAssigned to renderActivityPage(1) after the activity section initializes. Called on data updates and filter changes.
inventoryDataarrayIn-memory cache of normalized inventory aggregates. Populated by getInventoryData() and consumed by the inventory table renderer and filters.
activityDataarrayIn-memory cache of normalized activity aggregates. Populated by getActivityData() and consumed by the activity table renderer, filters, and export functions.
inMemoryShipmentStorearrayFallback shipment store used when Local Storage is unavailable or throws. Only updated inside the catch block of saveStoredShipments() — not written on every successful save.
notificationStatearrayIn-memory list of notification objects (id, type, title, description, meta, time, read). Declared with let inside the DOMContentLoaded callback (not a module-level global). Persisted to and loaded from warehouseNotificationState in Local Storage.

Build docs developers (and LLMs) love