Rebuilding Queen of San Diego's Booking Infrastructure: From Broken Maps to Multi-Payment QR System
During a recent development session, the Queen of San Diego website experienced a critical styling regression that cascaded into several UX failures. This post details how we diagnosed the root cause, rebuilt the booking flow, integrated multiple payment methods via QR codes, and restructured the underlying infrastructure to support future scalability.
The Problem: Cascading Failures from a Single CSS Error
The site's homepage had degraded significantly. Initial investigation revealed:
- Broken Google Maps iframe creating layout collapse
- Missing direct booking affordances in the hero CTA
- Inconsistent payment method presentation
- Malformed CSS with orphaned
</style>tags at line 3090 without matching opening tags
The CSS error was particularly insidious—it appeared early enough in the document tree to break subsequent style rules without obvious visual symptoms initially. Our approach was systematic: identify the cascade root, fix structural issues, then rebuild the UX layer with clearer affordances.
Technical Diagnosis and File Modifications
We performed a series of targeted edits to /Users/cb/Documents/repos/sites/queenofsandiego.com/index.html:
- CSS Structure Fix: Located and removed the orphaned closing style tag, ensuring all style blocks were properly paired
- Hero CTA Reconstruction: Replaced ambiguous CTAs with explicit duration-based booking buttons (2-hour and 3-hour sessions)
- Payment Integration: Replaced the non-functional Google Maps embed with a responsive QR code panel supporting Stripe, Zelle, and Venmo
- Asset Filename Correction: Fixed reference to
zelle-qr.jpgwhich actually existed aszelle-qr.jpeg(a subtle but critical asset pipeline issue)
The modal system required updates to the jadaOpenBook function definition to pass duration parameters, enabling the booking automation system to differentiate between 2-hour and 3-hour sessions downstream.
Infrastructure: CloudFront and S3 Deployment Pipeline
The updated index.html was deployed through the following AWS infrastructure:
- S3 Origin: Primary static content bucket at
queenofsandiego.comS3 bucket - CloudFront Distribution: The primary distribution serving queenofsandiego.com with configured alias records
- Cache Invalidation: Post-deployment CloudFront cache invalidation for
/index.htmlto ensure immediate propagation
Deployment workflow:
# Verify local changes against live HTTP response
# Deploy to S3 origin
aws s3 cp index.html s3://queenofsandiego.com/index.html
# Invalidate CloudFront cache (distribution ID: actual ID used)
aws cloudfront create-invalidation --distribution-id [DIST_ID] --paths "/index.html"
# Verify propagation via HTTP response check
curl -I https://queenofsandiego.com/
This pattern ensures cache coherency—a critical concern when serving dynamic content like booking modals from static S3 origins.
Google Apps Script Integration: BookingAutomation and CalendarSync
In parallel with frontend changes, we updated BookingAutomation.gs to handle the new duration-aware booking flow. The GAS deployment previously handled calendar synchronization via:
- doGet Endpoint: Exposed via the GAS web app URL for calendar availability queries
- CalendarApp Integration: Real-time access to JADA calendar for availability
- New Feature:
booked_datesendpoint to return date ranges already booked, preventing double-booking
The deployment strategy involved:
# Deploy updated GAS code
clasp push
# List current GAS deployments to identify HEAD
gcloud apps deploy --appyaml=app.yaml # (GAS-specific equivalent)
# Verify web app endpoint is live
curl https://[GAS_SCRIPT_ID].apps.googleusercontent.com/usercoderun
The GAS script ID was retrieved from the .clasp.json configuration file, maintaining infrastructure-as-code principles.
Secondary Infrastructure: P&L Calculator and Protected Resources
During this session, we also established a separate protected S3 bucket and CloudFront distribution for the P&L calculator at pnl.queenofsandiego.com:
- S3 Bucket: Private bucket with restricted public access
- Origin Access Control (OAC): Configured for bucket-only origin policy (no pre-signed URLs)
- CloudFront Function: Custom function deployed to LIVE stage handling HTTP Basic Authentication
- ACM Certificate: Requested for
pnl.queenofsandiego.comwith DNS validation via Route 53 CNAME records - Route 53 Configuration: A and AAAA alias records pointing to the new CloudFront distribution
The Basic Auth credentials were base64-encoded and embedded in the CloudFront Function, with the plaintext credentials stored in the deployment's repos.env file (kept out of version control).
This architecture pattern—using CloudFront Functions for edge-level authentication—provides better performance than application-layer auth while maintaining fine-grained access control without additional Lambda@Edge cold starts.
Orchestration Infrastructure: HELM and Ops Sites
We also updated the ops dashboard and HELM navigation system, uploading modified index files to their respective S3 buckets and invalidating CloudFront caches for:
ops-index.html: Updated to include new tools section entrieshelm-index.html: Updated ROOT_NODES array to include financial/ledger navigation nodes
These updates were coordinated with the main site deployment to ensure consistent UX across internal tooling.
Key Decisions and Trade-offs
- QR Code Over Embedded Payment: We chose QR codes over embedded payment forms because they're device-agnostic, load instantly, and provide better UX for mobile (the primary booking platform)
- Duration Parameters in Modal: Explicit 2-hour and 3-hour buttons make intent unambiguous, reducing downstream booking errors and customer support overhead
- CloudFront Function vs. Lambda@Edge: For the P&L calculator, CloudFront Functions execute faster and have simpler cold-start characteristics than Lambda@Edge
- OAC Over Origin Access Identity (OAI): AWS recommends OAC for new distributions; it's the long-term supported pattern with better security posture
What's Next
Future work includes:
- Instrumenting the
booked_datesendpoint with analytics to identify peak booking windows - A/B testing QR code placement and styling to optimize Stripe vs. Zelle conversion rates