Building HELM: An Interactive Operations Dashboard for Real-Time System Visibility
We just deployed HELM — a force-directed graph visualization dashboard that maps every system touchpoint in Queen of San Diego's booking and operations pipeline. This post covers the architecture, infrastructure decisions, and implementation details of this single-page application.
What Problem Were We Solving?
Queen of San Diego's operational complexity spans multiple platforms:
- Booking channels (web, email campaigns, referral partners, organic search)
- Third-party integrations (Viator, Boatsetter, GetMyBoat, plus future platforms)
- Revenue tracking and money flow
- Crew dispatch and sail operations
- Internal dashboards (progress tracking, expense management, crew coordination)
Management needed a single unified view showing how these systems interconnect, data flows between them, and real-time health status of each component. The requirement: make it polished enough that an engineer at a top-tier tech firm would demo it to colleagues.
Architecture Decision: Force-Directed Graph in the Browser
We chose vis-network — a JavaScript library for network visualization — rather than building a backend-driven dashboard. Why?
- Self-contained: The entire application runs in a single HTML file with no build step, no Node.js dependency, no separate API calls during visualization
- Low latency: Physics simulation happens client-side; no server round-trip for pan/zoom/drag interactions
- Resilience: Graph visualization works even if some backend systems are down
- Deployment simplicity: One file to S3, one CloudFront distribution, one DNS record
The tradeoff: we embed node metadata directly in the HTML rather than fetching from a database. For a system this size, that's acceptable and actually advantageous for cold-start performance.
Embedding System Topology in HTML
The core of HELM is a JavaScript object graph defined in the HTML file at /Users/cb/Documents/repos/sites/helm/index.html:
const NODE_DATA = {
visitor: { label: "Visitor", type: "entry", color: "#FFD700" },
email_campaign: { label: "Email Campaign", type: "channel" },
web_search: { label: "Web Search", type: "channel" },
referral_partner: { label: "Referral Partner Code", type: "channel" },
website: { label: "Website (queenofsandiego.com)", type: "platform" },
booking_form: { label: "Booking Form", type: "process" },
viator: { label: "Viator", type: "integration", status: "live" },
boatsetter: { label: "Boatsetter", type: "integration", status: "live" },
getmyboat: { label: "GetMyBoat", type: "integration", status: "live" },
future_platform: { label: "Future Platform", type: "integration", status: "planned" },
revenue_tracker: { label: "Revenue Tracker", type: "dashboard" },
crew_dispatch: { label: "Crew Dispatch", type: "ops", ref: "gas_crewdispatch" },
sail_ops: { label: "Sail Operations", type: "ops" },
progress_dashboard: { label: "Progress Dashboard", type: "dashboard" },
expense_tracker: { label: "Expense Tracker", type: "dashboard" }
};
Each node includes metadata: display label, system type (for styling), and for GAS-backed systems, a reference to the Google Apps Script project file.
Edges define the data flow between systems:
const EDGES = [
{ from: "visitor", to: "email_campaign", label: "Email list" },
{ from: "visitor", to: "web_search", label: "SEO" },
{ from: "email_campaign", to: "website", label: "Click" },
{ from: "web_search", to: "website", label: "Organic traffic" },
{ from: "website", to: "booking_form", label: "Browse → Book" },
{ from: "booking_form", to: "viator", label: "Send booking" },
{ from: "viator", to: "revenue_tracker", label: "Revenue data" },
{ from: "booking_form", to: "crew_dispatch", label: "Assign crew" },
{ from: "crew_dispatch", to: "sail_ops", label: "Crew list" }
];
Real-Time Health Diagnostics
We embedded health probe logic directly in the page. Each system node polls its corresponding backend:
- GAS-based systems: Call
doHealthCheck()via Google Apps Script Web App endpoints - S3/CloudFront systems: HTTP HEAD requests to smoke-test availability
- Third-party integrations: Lightweight API pings
Results color the nodes in real-time: green for healthy, red for down, yellow for degraded. A health bar at the top of the page aggregates the overall system status.
Infrastructure: Deploying to helm.queenofsandiego.com
We created a dedicated subdomain deployment:
aws s3 mb s3://helm.queenofsandiego.com --region us-east-1
Uploaded the index.html:
aws s3 cp /Users/cb/Documents/repos/sites/helm/index.html \
s3://helm.queenofsandiego.com/index.html \
--content-type "text/html; charset=utf-8" \
--acl public-read
Created a CloudFront distribution with:
- Origin: S3 bucket
helm.queenofsandiego.com - Origin Access Control (OAC): Restrict direct S3 access; serve only through CloudFront
- Default root object:
index.html - Cache behavior: TTL 300 seconds (5 min), allowing frequent updates without full invalidation
- Security: TLS 1.2+, no query string caching (to preserve health check freshness)
Updated Route53 with an ALIAS record:
Type: A
Name: helm.queenofsandiego.com
Alias Target: [CloudFront Distribution Domain Name]
Alias Hosted Zone ID: [CloudFront Hosted Zone]
Evaluate Target Health: No
Verification completed in ~2.5 minutes once CloudFront propagated globally.
Single-File Design Pattern
Because HELM is entirely self-contained in one HTML file, we had to solve several challenges:
- CSS scope: Wrapped all styles in a
<style>tag with specific class selectors to avoid conflicts - JavaScript isolation: All code runs in the global scope, but we used IIFE (Immediately Invoked Function Expression) patterns to contain variable scope
- External dependencies: vis-network and vis-data loaded from CDN (unpkg