Building HELM: A Real-Time Operations Dashboard for Distributed Booking & Dispatch Systems

Over the past development session, we built and deployed HELM — an interactive operations visualization tool for Queen of San Diego's complex booking, fulfillment, and crew dispatch pipeline. This post details the architecture, infrastructure decisions, and technical patterns that went into creating a self-contained, physics-based graph visualization that runs entirely in the browser.

What We Built

HELM is a single-page HTML file deployed to helm.queenofsandiego.com that visualizes the entire customer-to-crew workflow as an interactive force-directed graph. The system maps:

  • Acquisition channels: email campaigns, organic web search, referral partner codes
  • Booking platforms: Viator, GetMyBoat, BoatSetter (with greyed-out nodes for platforms not yet live)
  • Internal dashboards: expense tracking, crew dispatch, scheduling
  • Operations: crew communications, booking fulfillment, financial reconciliation
  • Live health metrics: real-time node status (green/red) based on system probes

The visualization uses vis-network, a force-directed physics engine that renders nodes and edges in WebGL, allowing users to drag, pan, and zoom through hundreds of interconnected system components. Crucially, HELM is a single, self-contained HTML file — no build step, no external dependencies beyond CDN resources, making it trivial to iterate and deploy.

Technical Architecture

Client-Side Data Model

The HTML file embeds three core JavaScript objects:

  • NODE_DATA: a map of node IDs to display properties (label, category, color, icon)
  • EDGES: an array of directed edges between nodes, each with a label describing the relationship
  • DEPTH_CATS: a lookup table mapping node IDs to "depth" categories (acquisition, booking, ops, fulfillment) used for radial layout and filtering

For example, the crew dispatch node is defined as:

gas_crewdispatch: {
  label: 'Crew Dispatch',
  category: 'dispatch',
  color: '#FFD700',
  icon: '📡'
}

And edges from booking confirmation to crew dispatch are stored as:

{
  from: 'booking_confirmed',
  to: 'gas_crewdispatch',
  label: 'Dispatch crew / confirm availability'
}

Force-Directed Physics & Layout

vis-network's physics engine automatically positions nodes based on edge forces. We configured:

  • Repulsion: nodes push away from each other to avoid clutter
  • Spring forces: connected nodes are attracted to each other
  • Damping: prevents oscillation and stabilizes the layout over ~2 seconds

This approach is far superior to manual positioning — as the system grows, new nodes automatically find good positions without requiring layout adjustments.

Health Monitoring Integration

Each node can display live health status (green/red glow) by polling backend endpoints. The dashboard includes a system-wide health bar showing the percentage of healthy components. This is computed by:

  • Probing each node's underlying service (GAS functions, external APIs, databases)
  • Rendering the node's border color green (healthy) or red (unhealthy)
  • Aggregating into a top-level health percentage

For example, the makeVisNode function in the embedded script constructs a vis-network node object and applies health styling:

function makeVisNode(id, nodeData, healthStatus) {
  return {
    id: id,
    label: nodeData.label,
    color: healthStatus === 'healthy' ? '#2E8B57' : '#DC143C',
    borderWidth: 3,
    // ... physics and interaction properties
  }
}

Drill-Down Detail Panel

When users click a node, a side panel appears listing:

  • All inbound and outbound edges (with labels describing the data flow)
  • Associated Google Apps Script function signatures (e.g., dispatchCrewForSail(sailId, crewList))
  • Real-time status and last-updated timestamp

This turns the graph into a navigation aid for engineers diving into source code or debugging a specific booking flow.

Infrastructure & Deployment

S3 & CloudFront Setup

HELM is deployed as a static asset to a dedicated S3 bucket with CloudFront CDN in front:

  • S3 bucket: helm.queenofsandiego.com (created via AWS CLI)
  • CloudFront distribution: Origin Access Control (OAC) for secure, bucket-policy-enforced access
  • Route53 alias: helm.queenofsandiego.com → CloudFront domain
  • Cache behavior: 60-second TTL for the HTML file (aggressive invalidation on deploy)

The bucket policy restricts access to CloudFront only, preventing direct S3 URL access:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::helm.queenofsandiego.com/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::ACCOUNT_ID:distribution/DISTRIBUTION_ID"
        }
      }
    }
  ]
}

Deployment Pipeline

The deployment workflow is straightforward:

  1. Edit /Users/cb/Documents/repos/sites/helm/index.html locally
  2. Validate HTML/CSS/JavaScript syntax with a runtime eval against mock browser APIs
  3. Upload to S3: aws s3 cp index.html s3://helm.queenofsandiego.com/
  4. Invalidate CloudFront cache: aws cloudfront create-invalidation --distribution-id DIST_ID --paths "/*"
  5. Smoke test via CloudFront domain (not S3 direct URL)

This approach ensures zero downtime and leverages CloudFront's global edge locations for sub-100ms latency worldwide.

Key Technical Decisions

Single HTML File vs. Modular Build

We chose a monolithic HTML file (no build step, no webpack, no Node.js toolchain) because:

  • Iteration speed: edit, save, deploy in <30 seconds
  • Operational simplicity: one artifact to version,