Building a Real-Time Operational Dashboard with Hash-Based Deep Linking and Orchestrator Integration
Over the last development sprint, we built and deployed a centralized operational dashboard at progress.queenofsandiego.com that aggregates Google Analytics audits, email campaign tracking, and multi-platform health checks into a single Kanban-style interface. This post covers the technical architecture, why we chose hash-based routing over server-side navigation, and how we integrated an autonomous orchestrator agent to run background audits and surface urgent items as actionable cards.
The Problem: Scattered Operational Visibility
Before this project, operational status lived in multiple places: GA insights scattered across spreadsheets, email blast approvals buried in Slack threads, infrastructure gaps documented in wiki pages that nobody read. When urgent items like a Mother's Day campaign needed approval with 4 days to launch, there was no single source of truth. We needed a system that could:
- Aggregate data from Google Analytics, Constant Contact, and internal dashboards in real time
- Surface high-priority blockers (unapproved campaigns, missing GA tracking, infrastructure gaps) as cards the team notices immediately
- Generate deep links to specific cards so engineers can share findings without context switching
- Run autonomous background audits without blocking the UI
Architecture: Client-Side Routing with Hash Navigation
We chose a client-side hash-based routing pattern instead of server-side routing for several reasons. First, our dashboard is a static asset served from CloudFront (distribution ID: E2XMPL1ABC123), which minimizes latency across regions. Second, hash navigation doesn't require server-side logic to map URLs to templates—the browser handles route changes locally. Third, all dashboard card data is fetched from our orchestrator API and cached in-browser, so we don't need to rebuild the HTML for each view.
The deep link format follows this pattern:
https://progress.queenofsandiego.com/#card-{card-id}
For example, a card with ID t-31aa2593 can be shared directly as:
https://progress.queenofsandiego.com/#card-t-31aa2593
This is handled by the dashboard's main JS module (/assets/js/dashboard-router.js), which listens to hashchange events and scrolls to the corresponding card element in the DOM. The implementation is straightforward:
window.addEventListener('hashchange', function() {
const hash = window.location.hash;
if (hash.startsWith('#card-')) {
const cardId = hash.substring(6);
const cardElement = document.getElementById(cardId);
if (cardElement) {
cardElement.scrollIntoView({ behavior: 'smooth' });
cardElement.classList.add('highlight-focus');
}
}
});
Why not use the History API with semantic URLs like /card/t-31aa2593? Because our CDN doesn't support server-side URL rewriting without adding Lambda@Edge functions, which would increase latency and operational overhead. Hash-based routing gave us deep linking without infrastructure complexity.
The Orchestrator: Background Audits and Card Generation
The real magic happens in the orchestrator agent, which runs as a scheduled Lambda function that executes every 6 hours. This agent:
- Audits every HTML file across all platforms (
/repos/web,/repos/email-templates,/repos/api-docs) for Google Analytics tracking codes - Pulls last-30-days traffic data from GA (once API access is granted)
- Queries Constant Contact for scheduled email campaigns and approval status
- Generates a markdown report with findings and recommendations
- Creates or updates dashboard cards based on urgency
The orchestrator invocation looks like this:
aws lambda invoke \
--function-name orchestrator-audit-runner \
--payload '{"audit_type":"full","scope":["ga_codes","campaigns","infrastructure"]}' \
/tmp/response.json
The Lambda function writes the generated report to S3 at:
s3://queen-of-diego-orchestrator/reports/{timestamp}/audit-report.md
It then POSTs to the dashboard API (https://progress.queenofsandiego.com/api/cards) to create or update cards. Each card includes:
id: A unique identifier (format:t-{8-char-hex})title: Human-readable summary (e.g., "Mother's Day Blast Needs Approval")priority: One ofcritical,high,medium,lowsection: Where it appears on the board (e.g., "blockers", "campaigns", "infrastructure")description: Full details rendered as markdowndeep_link: The hash URL for sharing
The card payload structure:
POST /api/cards
Content-Type: application/json
{
"id": "t-31aa2593",
"title": "Mother's Day Campaign Awaiting Approval",
"priority": "critical",
"section": "campaigns",
"description": "Scheduled for April 29 (4 days). No approval signature. Constant Contact draft status.",
"deep_link": "https://progress.queenofsandiego.com/#card-t-31aa2593"
}
GA Audit: Finding Tracking Gaps
The GA code audit recursively scans all HTML files looking for Google Analytics tags. The audit checks for:
- GA4 measurement ID in the format
G-{10-char-alphanumeric} - gtag.js script tags (modern GA implementation)
- Proper page view event firing on navigation
Files checked include:
/repos/web/public/**/*.html(main website)/repos/email-templates/**/*.html(email templates; these typically don't need GA, but we flag if they do for web-view links)/repos/api-docs/index.html
The audit command generated during the session was:
find /Users/cb/Documents/repos -name "*.html" -type f -exec grep -l "gtag\|GA-\|G-[A-Z0-9]\{10\}" {} \; | sort
Missing GA codes surface as high-priority cards so the team can add tracking before traffic is lost.
Email Campaign Tracking via Constant Contact
The orchestrator integrates with Constant Contact's API to fetch campaign metadata. It looks for:
- Scheduled campaigns and their send dates
- Approval status (draft, pending review, approved, sent)
- Contact list size and engagement metrics from recent sends
Campaigns marked "pending review" or "draft" with send dates within 7 days trigger critical