```html

Building a Deep-Link Dashboard for Real-Time Campaign Orchestration

Over the past development session, we implemented a hash-based deep-linking system for our campaign management dashboard, integrated it with an automated orchestrator for Google Analytics audits, and wired up real-time kanban-style task cards. This post walks through the technical architecture, infrastructure decisions, and patterns we used to make campaign status visible and actionable across distributed teams.

What Was Done

We built three interconnected pieces:

  • A hash-navigation deep-linking system in the dashboard that maps card IDs to anchor fragments, allowing shareable URLs like https://progress.queenofsandiego.com/#card-t-31aa2593
  • An automated GA audit agent that sweeps all HTML across multiple sites, validates tracking code presence, and pulls 30-day traffic data
  • A kanban card generation pipeline that surfaces audit findings, campaign deadlines, and operational blockers as actionable dashboard cards with deep-link references

The result: when a campaign needs attention (like a Mother's Day blast 4 days before launch), it shows up on the dashboard with a direct link, no hunting required.

Technical Details: Hash Navigation System

The dashboard HTML lives at /Users/cb/Documents/repos/memory/feedback_dashboard_deep_links.md and the underlying JS handles client-side routing. Rather than server-side path routing, we use the fragment identifier pattern—everything after # in the URL stays client-side and doesn't trigger a page reload.

The deep link format is:

https://progress.queenofsandiego.com/#card-{cardId}

When a user lands on this URL, the dashboard JS reads the hash, extracts the card ID, and scrolls/renders that card in focus. This pattern was chosen because:

  • No server round-trip: The dashboard doesn't need server logic to handle anchors; the browser handles fragment navigation natively
  • Shareable: Copy the URL from your browser—it's a valid, bookmarkable link
  • Stateless: Each card is independently accessible; no session cookie required
  • SEO-friendly alternative exists: If we need crawlable URLs later, we can add server-side rendering without breaking existing links

The JS checks the URL on load and on every hash change event:

window.addEventListener('hashchange', handleCardNavigation);
const cardId = window.location.hash.replace('#card-', '');
if (cardId) scrollToCard(cardId);

Orchestrator + GA Audit Pipeline

The orchestrator agent (documented in /Users/cb/Documents/repos/memory/MEMORY.md) handles a multi-stage audit:

  • Stage 1: GA Code Sweep — Scans all HTML files across repos, checks for GA tracking snippets (both Universal Analytics and GA4 formats), and flags pages missing instrumentation
  • Stage 2: Traffic Data Pull — Attempts to query the GA Data API for the last 30 days of traffic by page and campaign
  • Stage 3: Campaign Check — Reads the Constant Contact export CSV (located via find_contacts_csv_path_logic) and cross-references scheduled campaigns against the calendar
  • Stage 4: Report Generation — Bundles findings into structured card objects and posts them to the dashboard

The audit discovered three urgent blockers:

  1. Mother's Day blast unapproved — Scheduled for April 29, event in 4 days, campaign log shows 0 contacts marked sent. Generated card t-31aa2593 with deep link.
  2. Paul Simon blast proof deadline — Proof needed by May 12 (6 days); template located at /repos/email_templates/paul_simon_blast.html, subject line cached from template scan.
  3. GA API access missing — Service account has no Data API permissions. Fix: grant access in GA Admin console (3-minute task, documented in card notes).

Infrastructure & Card Data Model

Cards are stored as JSON objects in a DynamoDB table (no path disclosed here for security). Each card has:

{
  "cardId": "t-31aa2593",
  "type": "needs-you",
  "title": "Mother's Day Blast Awaiting Approval",
  "deadline": "2024-04-29T00:00:00Z",
  "sections": [
    {"title": "Campaign Details", "content": "..."},
    {"title": "Current Status", "content": "..."},
    {"title": "Action Required", "content": "..."}
  ],
  "deepLink": "https://progress.queenofsandiego.com/#card-t-31aa2593"
}

The dashboard renders these cards and the hash anchor system ensures users land directly on the card when they click the deep link. The CloudFront distribution (serving progress.queenofsandiego.com) caches the dashboard HTML with a short TTL so new cards appear within seconds of generation.

GA Code Audit Command Examples

To replicate the GA code sweep across all repos:

find /path/to/repos -name "*.html" -o -name "*.jsx" -o -name "*.js" | \
  xargs grep -l "gtag\|_gaq\|ga(" | \
  while read file; do echo "$file"; grep -o "GA-[0-9]\+" "$file" || echo "NO_GA_CODE"; done

For traffic data (once GA API access is granted):

curl -X POST https://analyticsreporting.googleapis.com/v4/reports:batchGet \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reportRequests": [{
      "viewId": "VIEW_ID",
      "dateRanges": [{"startDate": "30daysAgo", "endDate": "today"}],
      "metrics": [{"expression": "ga:users"}, {"expression": "ga:pageviews"}],
      "dimensions": [{"name": "ga:pagePath"}, {"name": "ga:source"}]
    }]
  }'

Key Decisions

  • Hash navigation over query params: Query params would require server-side URL parsing; hash params are fully client-side and don't pollute server logs with unique URLs.
  • Kanban cards over email alerts: A dashboard card is persistent, linkable, and visible to the whole team. Email gets buried; a card on the board stays until it's resolved.
  • Orchestrator agent over cron jobs: The agent can reason about context (e.g., "this campaign is 4 days away, so it needs approval now"), whereas a cron job just runs on a schedule.
  • DynamoDB for cards: Fast reads, supports TTL for auto-expiring cards, scales to thousands of concurrent dashboard users without provisioning.

What's Next

  • Grant GA Data API access — Add service account to GA Admin console so