Fixing Cross-Site GA Contamination and Rebuilding the SailJADA Booking Modal

What Was Done

During a production audit of four properties managed across the same AWS account, we discovered two critical issues:

  • GA Tag Ownership Problem: QuickDumpNow.com's Google Analytics and Search Console properties were owned by the JADA business account (jadasailing@gmail.com) instead of the QDN admin account, creating organizational and compliance risk.
  • Modal UX Regression: SailJADA's booking widget on /crew/ and other subpages had fallen out of sync with the sophisticated modal implementation on QueenOfSanDiego.com's homepage, resulting in broken date-picker functionality and poor user experience.

This post covers the technical approach to both: how we isolated GA tag contamination across 17 SailJADA subpages, rebuilt a shared modal asset, and deployed it safely through staging before promoting to production.

Technical Details: GA Tag Audit

The root cause investigation revealed a structural problem: while homepage GA tags were correct (JADA = G-N6HKL4KLKT, QDN = G-539T97NM1Z), three QDN subpages were missing their GA tag entirely:

  • /book/
  • /track/
  • /service-areas/carlsbad/

We scanned all four properties' subpages using a Python audit script that fetched HTML from S3, parsed the <script> tags, and logged which GA property ID appeared on each page. The script iterated across all depth-1 and depth-2 pages in each S3 bucket, comparing actual tags against expected ownership.

Why this matters: Missing GA tags mean conversion data for those pages never reaches QDN's analytics. Cross-contamination (JADA tags on QDN pages) would pollute JADA's funnel analysis with non-JADA traffic.

Technical Details: Modal Reconstruction

The SailJADA booking modal had degraded because the shared modal asset was not being loaded on subpages. We reverse-engineered the working implementation from QueenOfSanDiego.com's homepage:

Source Investigation

The QueenOfSanDiego.com homepage (index.html) contained an embedded modal definition with:

  • HTML structure with anchor IDs for modal triggers
  • CSS with F-string template variables that rendered during build
  • Inline JavaScript handling date selection and Stripe integration
  • Script tag references to Stripe.js and Google Analytics

The F-string brace bug manifested as malformed CSS: template variables like {{ variable }} were not being escaped during the build process, breaking the booking calendar's styling.

Extraction and Refactoring

We extracted the modal logic into a shared asset jada-modal.js deployed to:

  • S3 path: s3://sailjada-www-prod/assets/jada-modal.js
  • Staging equivalent: s3://sailjada-www-staging/assets/jada-modal.js

The refactored module wrapped the original code in an IIFE (Immediately Invoked Function Expression) to avoid global namespace pollution, then exposed only necessary functions as module properties. We identified three global dependencies that subpages needed:

  • openBookingModal() — triggered by CTA buttons
  • closeBookingModal() — dismiss handler
  • initDatePicker() — calendar initialization

Each subpage's HTML received a script tag that loaded jada-modal.js and called the initialization function in a deferred context to ensure DOM readiness.

Infrastructure and Deployment

S3 and CloudFront Architecture

Both SailJADA and QueenOfSanDiego operate under a dual-environment pattern:

  • Staging: s3://sailjada-www-staging/ and s3://queenofsandiego-www-staging/
  • Production: s3://sailjada-www-prod/ and s3://queenofsandiego-www-prod/

CloudFront distributions sit in front of each bucket, providing:

  • Cache invalidation control (critical for pushing updates without waiting for TTL expiry)
  • HTTPS enforcement
  • Geo-restriction policies aligned with service areas

The deployment script deploy_qos.sh handles syncing local source files to S3 and triggering CloudFront invalidations. It includes a linting gate: if lint_format_template.py fails with exit code 4, the deploy aborts and rolls back.

Patching 17 Subpages

SailJADA has 16 depth-1 subpages plus the homepage. We patched each one by:

  1. Pulling the current production version from S3
  2. Injecting the <script src="/assets/jada-modal.js"></script> tag in the <head>
  3. Converting all CTA button classes from legacy selectors to btn-reserve (standardized across all pages)
  4. Fixing relative image paths (e.g., Zelle QR code references) to absolute S3 paths
  5. Syncing to staging bucket and validating in staging environment
  6. Running smoke tests through the staging CloudFront distribution
  7. Snapshotting production state before promoting
  8. Syncing to production and invalidating exact paths in CloudFront

The invalidation strategy was critical: we invalidated exact file paths (e.g., /crew/index.html, /assets/jada-modal.js) rather than wildcard invalidations, which prevents unnecessary cache purges and keeps costs low.

Key Decisions

Why a Shared JS Asset Instead of Inline Code

Inlining the modal code into each of 17 subpages would have created maintenance hell. A single shared asset means:

  • Bug fixes propagate to all pages via a single deploy
  • Browser caching of the 40KB asset across page navigation
  • Easier A/B testing and feature flags

Why Staging First

After a previous production crash, all deployments now go through staging with full smoke tests. We validated:

  • Modal opens when buttons are clicked
  • Date picker renders and allows date selection
  • Stripe integration fires (verified in browser console