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 relationshipDEPTH_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:
- Edit
/Users/cb/Documents/repos/sites/helm/index.htmllocally - Validate HTML/CSS/JavaScript syntax with a runtime eval against mock browser APIs
- Upload to S3:
aws s3 cp index.html s3://helm.queenofsandiego.com/ - Invalidate CloudFront cache:
aws cloudfront create-invalidation --distribution-id DIST_ID --paths "/*" - 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,