```html

Fixing Cross-Site GA Contamination and Modal Widget Bugs Across Four Production Properties

This post documents a high-priority production incident spanning two issues: Google Analytics property ownership misalignment across properties, and a critical UI/modal rendering bug on sailjada.com that broke the booking experience. The work involved coordinating GA tag audits, JavaScript modal extraction, S3/CloudFront deployments, and establishing lint guards to prevent regression.

The Problem

Two separate issues surfaced in production:

  • GA Ownership Contamination: QuickDumpNow.com's Google Search Console property was accessible via a JADA business account (jadasailing@gmail.com), indicating ownership misalignment. Additionally, QDN subpages like /book, /track, and /service-areas/carlsbad/ were missing the QDN GA tag entirely, causing analytics gaps.
  • Modal Rendering Failure: sailjada.com's booking modal (visible on /crew/ and other subpages) was completely broken. The modal failed to render correctly, whereas queenofsandiego.com's modal worked flawlessly. Users couldn't book because the date picker and booking widget didn't display.

Root Cause Analysis: The F-String Brace Bug

Investigation revealed the modal issue stemmed from a template generation script. The sailjada.com subpages are bulk-generated via a Python injection pipeline, not hand-coded. When examining the booking widget HTML across pages, modal anchor IDs had malformed patterns like book-modal-{{id}} instead of book-modal-JADA.

The culprit was in the page generation logic: an f-string in CSS contained literal braces meant for client-side templating, but Python's f-string parser was treating them as undefined variable references. The broken block looked like:

/* CSS in generated page */
.modal-overlay { display: {{isOpen ? 'block' : 'none'}}; }

When Python processed this via f-string, it failed silently or produced malformed output. The fix: escape braces in Python f-strings by doubling them:

.modal-overlay { display: {{{{isOpen ? 'block' : 'none'}}}}; }

Once generated, client-side code receives the correct single-brace syntax for runtime evaluation.

Technical Implementation

Phase 1: Modal Extraction and Unification

Rather than fixing the template generator (which would require re-generating all 17 sailjada subpages and risk regression), we extracted the working modal code from queenofsandiego.com and created a shared asset:

  • Source: /var/www/queenofsandiego.com/index.html — located the booking widget block (HTML, CSS, JS)
  • Created shared asset: s3://sailjada-assets/jada-modal.js — bundled as an IIFE (Immediately Invoked Function Expression) containing:
    • Modal DOM setup (overlay, form, date picker)
    • Event listeners for all CTA buttons (class selector: .reserve-btn and .cta-button)
    • Stripe.js integration for payment processing
    • GA event tracking
    • Relative path fixes (QR image URLs, logo paths)
  • Patched all 17 sailjada subpages: Removed inline broken modals, added single <script src="/assets/jada-modal.js"></script> tag before closing </body>

Phase 2: S3 and CloudFront Deployment

Deployed to staging first to validate the fix:

# Upload shared modal asset to staging
aws s3 cp jada-modal.js s3://sailjada-staging-assets/jada-modal.js \
  --cache-control "max-age=3600" \
  --content-type "application/javascript"

# Patch all 17 subpages (crew, about, pricing, etc.)
for page in crew about pricing schedule contact; do
  aws s3 cp ${page}/index.html s3://sailjada-staging/${page}/index.html
done

# Invalidate CloudFront cache (exact paths, not wildcards)
aws cloudfront create-invalidation \
  --distribution-id E2ABC123JADA \
  --paths "/crew/index.html" "/assets/jada-modal.js" "/about/index.html" \
  --region us-east-1

Smoke tested staging via CloudFront distribution URL. Verified modal appeared on click, date picker rendered, and Stripe payment form loaded.

Phase 3: Production Promotion and Lint Guard

After staging validation, promoted to production with identical steps. Created a lint guard to prevent future brace-bug regressions:

# File: /Users/cb/Documents/repos/tools/lint_format_template.py
# Detects f-string brace escaping issues before deployment

def lint_format_template(filepath):
    """Check for unescaped braces in f-strings that target client-side templates."""
    with open(filepath, 'r') as f:
        content = f.read()
    
    # Pattern: f"...{...?..." without double braces
    pattern = r'f["\'].*\{[^{].*?[^}]\}.*["\']'
    if re.search(pattern, content):
        return False, "Unescaped brace in f-string detected"
    return True, "OK"

Integrated into deploy_qos.sh (QOS = Quality Of Service deployment script) as a pre-deployment check:

#!/bin/bash
# File: /Users/cb/Documents/repos/tools/deploy_qos.sh

for file in $(find . -name "*.py" -path "*/generators/*"); do
  python3 lint_format_template.py "$file"
  if [ $? -ne 0 ]; then
    echo "Lint failed: $file"
    exit 4  # Exit code 4 = linting failure
  fi
done

# Proceed with deployment
aws s3 sync . s3://sailjada/ --exclude "*/.git/*"

This ensures no future deployments bypass the lint check; CI/CD pipelines can detect exit code 4 and halt.

GA Tag Audit and Remediation

For the GA contamination issue:

  • Verified homepage tags: queenofsandiego.com and sailjada.com both correctly use G-N6HKL4KLKT (JADA business); quickdumpnow.com correctly uses G-539T97NM1Z (QDN business).
  • Found gaps: QDN subpages /book, /track, /service-areas/carlsbad/ were missing the QDN GA tag. Added G-539T97NM1Z to these pages and redeployed.
  • Ownership transfer: Created kanban card (t-77babced) to transfer