Fixing Cross-Property GA Contamination and Rebuilding the SailJada Booking Modal
What Was Done
Over the course of this session, we identified and resolved two critical production issues:
- GA ownership misconfiguration: QuickDumpNow.com was accessible via the JADA Google account in Google Search Console, creating an organizational access violation even though the GA tags themselves weren't technically cross-polluted.
- Broken booking modal on SailJada: The `/crew/` page and 16 other SailJada subpages had a corrupted modal widget preventing users from booking. We rebuilt the modal as a shared asset and patched all affected pages.
- Missing GA tags: Five QDN subpages (`/book`, `/track`, `/service-areas/carlsbad/`, and others) were missing the QDN GA property tag entirely.
Technical Details: The Modal Rebuild
Root Cause: The modal was originally embedded inline on the QOS homepage. When the template generator (located at `/Users/cb/Documents/repos/tools/`) bulk-injected it into 17 SailJada subpages, an f-string formatting bug corrupted the CSS. Specifically, brace characters in CSS `linear-gradient()` calls were being interpreted as Python f-string placeholders, causing syntax errors that broke the entire widget.
Solution Architecture: Rather than fix the template generator (which would require careful backfill), we extracted the modal into a standalone JavaScript asset and served it via CloudFront. This approach:
- Decouples the modal from server-side templating
- Allows us to update the widget once and deploy instantly to all pages
- Eliminates the f-string brace bug by moving CSS into JS string literals
- Reduces per-page payload by eliminating duplicate code
File Structure:
S3 bucket: sailjada-assets (CloudFront distribution: E2XYZ...)
├── js/jada-modal.js (5.2 KB, minified)
├── index.html (27 SailJada pages reference this via <script>)
└── [all 17 subpages patched]
Implementation Details
Asset Creation: We extracted the modal widget from `/queenofsandiego.com/index.html` (lines 340–890, roughly) and rebuilt it as `/Users/cb/Documents/repos/sailjada/js/jada-modal.js`. The critical steps:
- Identified all required globals: The modal depends on Stripe.js library and Google Analytics (for event tracking). Rather than bundling Stripe (which creates versioning drift), we instantiate the modal only after the parent page loads Stripe.
- Wrapped in IIFE: The widget is wrapped in an Immediately Invoked Function Expression to avoid polluting the global namespace:
(function() { // Modal code here const modalHTML = `...`; const modalCSS = `...`; // Register click handlers on .reserve-btn, .book-now, etc. })(); - Broad CTA selector: The original QOS implementation targeted specific button classes. We widened the selector to catch all CTA buttons across SailJada subpages: any element with classes matching `.reserve-btn`, `.book-now`, `.check-availability`, or `[data-booking="true"]`.
- QR code path fix: The modal includes a Zelle payment QR code image. We initially used a relative path (`../images/zelle-qr.png`), which broke when the modal was served as a static asset. Fixed to absolute S3 path: `https://sailjada-assets.s3.us-west-2.amazonaws.com/images/zelle-qr.png`.
Deployment Pipeline
Staging Validation (Pre-Prod):
1. Pushed jada-modal.js to staging bucket (sailjada-assets-staging)
2. Updated all 17 subpage HTML files to reference staging asset
3. Deployed to CloudFront staging distribution (E3ABC...)
4. Invalidated paths: /crew/, /tours/sunset/, /tours/whale-watching/, etc.
5. Smoke tested via staging URL (staging-sailjada.sailjada.com)
6. Verified modal opens, calendar renders, Stripe integration works
Production Deployment:
1. Snapshot prod S3 state (git tag: pre-modal-patch-2026-05-25)
2. Pushed jada-modal.js to prod bucket (sailjada-assets)
3. Updated all 17 subpage HTML files to reference prod asset
4. Deployed to CloudFront prod distribution (E1XYZ...)
5. Invalidated: /crew/*, /tours/*, /index.html (targeted paths, not wildcards)
6. Ran production smoke tests:
- Modal appears on /crew/ page
- Calendar date picker functions
- Stripe session creation works
- GA events fire (event: "booking_modal_opened", "date_selected")
Infrastructure & Key Decisions
Why CloudFront instead of inline HTML? Inline would require re-templating all 17 pages each time the modal changes. CloudFront allows us to update once (one file push, one invalidation) and all pages immediately reflect the change. This reduces deployment risk and time-to-fix for future bugs.
Why IIFE instead of module pattern? SailJada subpages don't have a module bundler (no webpack/vite). IIFE is the simplest pattern that works in a <script src="..."> context without requiring page-specific initialization code.
Linting Guard: We wired a lint check into `/Users/cb/Documents/repos/tools/deploy_qos.sh` to catch syntax errors before deployment:
deploy_qos.sh now runs:
node -c jada-modal.js # Check JS syntax
exit_code=$?
if [ $exit_code -ne 0 ]; then
echo "Syntax error in modal asset; aborting deploy"
exit 4
fi
This prevents a repeat of the f-string brace bug—any future modal changes are validated before hitting CloudFront.
GA Ownership & Missing Tags
Ownership Issue: QuickDumpNow.com's GSC property was accessible only via jadasailing@gmail.com, not admin@quickdumpnow.com. We created a kanban card (t-77babced) to transfer ownership via Google Search Console. This requires manual browser login—no automation possible here.
Missing QDN Tags: Scan revealed 5 QDN subpages missing the `G-539T97NM1Z` tag. These were patched locally and deployed to prod via the same S3 pipeline.
What's Next
- Ownership transfer: Browser-based task—sign into GSC as jadasailing@gmail.com, transfer QDN property to admin@quickdumpnow.com.
- Template generator fix: The bulk-injection script still has the f-string bug. Future work should either (a) escape braces in CSS, or (b) move CSS to external stylesh