Fixing Cross-Property Analytics Contamination and Rebuilding the SailJada Booking Modal
What We Did
We identified and began remediating a critical analytics ownership issue across four distinct properties (SailJada, Queen of San Diego, Quick Dump Now, and related sites), then rebuilt the SailJada booking modal to match production-grade UX standards from the Queen of San Diego homepage.
The Analytics Problem: Ownership vs. Data Contamination
A Google Search Console alert flagged that quickdumpnow.com was being accessed through the jadasailing@gmail.com account. This revealed a deeper architectural issue: multiple independent business properties were co-mingled under a single Google account.
What we found:
sailjada.com– GA tagG-N6HKL4KLKT(JADA property)queenofsandiego.com– GA tagG-N6HKL4KLKT(same JADA property, intentional)quickdumpnow.com– GA tagG-539T97NM1Z(QDN property, different business)dangerouscentaur.com– GA tagG-GGP987FJSW(separate property)86from.com– GA tagG-YJWFKWVWNQ(separate property)burialsatseasandiego.com– UA tagUA-175171552-1(legacy, outdated)
Critical finding: QDN subpages at /book, /track, and /service-areas/carlsbad/ were missing the QDN GA tag entirely, potentially causing data loss. Separately, the GSC property for QDN was owned by a JADA email account, creating an access control violation.
Why this matters: Analytics data is now split across properties, making it impossible to accurately track QDN user behavior. More importantly, a third-party property (QDN) is under JADA's Google account control, creating compliance and ownership ambiguity.
Infrastructure Auditing Approach
We used Python to scan all subpages across all properties, checking for GA tag presence and correct property assignment:
#!/usr/bin/env python3
import re
import sys
GA_PATTERN = r'G-[A-Z0-9]{10}|UA-\d+-\d+'
def scan_file(filepath, expected_tag=None):
try:
with open(filepath, 'r') as f:
content = f.read()
matches = re.findall(GA_PATTERN, content)
return matches
except Exception as e:
return []
This revealed that QDN's inner pages had zero GA instrumentation, while JADA properties had consistent tagging. We mapped all findings into a kanban card for ownership transfer.
The SailJada Booking Modal Rebuild
The production crew page at sailjada.com/crew/ had a broken, unprofessional booking interface. The requirement was to extract the battle-tested modal implementation from queenofsandiego.com and replicate it across all SailJada subpages.
Source Analysis: Queen of San Diego Modal Pattern
We pulled the Queen of San Diego homepage and identified the modal architecture:
- HTML structure: Booking widget block in the main page body
- CSS: Modal styling, overlay effects, and calendar grid layout
- JavaScript: Event listeners on CTA buttons, modal state management, date picker logic
- External deps: Stripe.js for payment processing, GA event tracking
The modal was not a globally available asset—it was baked into the homepage. To make it work across 17 SailJada subpages, we needed to extract and refactor it as a shared module.
Building the Shared Asset: jada-modal.js
We extracted the modal logic into /assets/js/jada-modal.js with an Immediately Invoked Function Expression (IIFE) pattern to avoid global namespace pollution:
(function() {
const modalConfig = {
selector: '[data-jada-modal-trigger]',
stripeKey: 'pk_live_xxx',
gaId: 'G-N6HKL4KLKT'
};
function initModal() {
document.querySelectorAll(modalConfig.selector).forEach(btn => {
btn.addEventListener('click', showBookingModal);
});
}
function showBookingModal(e) {
e.preventDefault();
// Modal state management and calendar initialization
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initModal);
} else {
initModal();
}
})();
Why IIFE? Prevents variable collisions when the same script is loaded on multiple pages. Functions stay scoped and can't accidentally override page-level globals.
HTML Pattern Across Subpages
Every CTA button now uses a consistent data attribute:
<button data-jada-modal-trigger class="btn btn-primary">
Check Availability
</button>
No inline onclick handlers. No page-specific event binding. Just a declarative HTML attribute that the shared asset recognizes on load.
Deployment: Staging First, Then Production
S3 Structure:
- Staging bucket:
sailjada-staging.s3.us-west-2.amazonaws.com - Production bucket:
sailjada-prod.s3.us-west-2.amazonaws.com
CloudFront Invalidation: Rather than wildcards (/*), we invalidated exact paths to avoid cache thrashing:
aws cloudfront create-invalidation \
--distribution-id E2ABC123STAGING \
--paths "/crew/index.html" "/assets/js/jada-modal.js" "/assets/css/modal.css"
Why exact paths? CloudFront processes wildcard invalidations as full distribution flushes. For a large site, that's expensive and slow. Exact paths are instant and cheap.
The Staging Verification
We smoke-tested the staging deployment by hitting the CF distribution directly and verifying:
- Modal JavaScript loads without syntax errors
- Stripe.js initializes correctly
- GA events fire when a button is clicked
- Modal appears and date picker responds to interaction
- QR code for Zelle payments renders (fixed relative path from
../images/zelle-qr.pngto absolute/assets/images/zelle-qr.png)