Skip to content

Add analytics tracking and response data collection#63

Merged
jeremymanning merged 10 commits intomainfrom
010-analytics-data-collection
Apr 1, 2026
Merged

Add analytics tracking and response data collection#63
jeremymanning merged 10 commits intomainfrom
010-analytics-data-collection

Conversation

@jeremymanning
Copy link
Copy Markdown
Member

Summary

  • GoatCounter analytics: Cookie-free visitor tracking via lightweight script tag (context-lab.goatcounter.com)
  • Response collection: Anonymized quiz responses encoded as tokens and sent to a Google Sheet via Google Apps Script every 10 responses, plus a beforeunload beacon for partial sessions
  • Tutorial consent step: "Contribute to science!" modal at end of tutorial lets users opt in/out
  • About modal toggle: Data collection disclosure text with persistent opt-out switch
  • Offline decoder: scripts/decode-tokens.js decodes collected tokens to CSV/JSON with per-question domain derivation
  • Citation update: Updated to published Nature Communications 17:2055 DOI
  • Tutorial positioning fixes: Deep Dive/Exploring Domain modals positioned left of quiz panel; header button highlights now dim full page correctly

Collection is off by default — users opt in via the tutorial or About modal. No PII is collected (tokens contain only question indices and answer correctness). Feature-flagged via CONFIG.ENABLED and ENDPOINT_URL.

Test plan

  • Unit tests pass (110/110, Vitest)
  • GoatCounter snippet loads without errors (skips localhost correctly)
  • Response collection verified end-to-end: 10-question token appears in Google Sheet
  • Beforeunload beacon verified: partial session (11 responses) captured via fetch keepalive
  • Token round-trip fidelity: 100% (encode in browser → decode via script → all fields match)
  • Tutorial consent step: "I'd like to help!" enables collection, "No thanks" disables it
  • About modal toggle: syncs with tutorial choice in real-time, persists across sessions
  • Opt-out verified: toggling off prevents all collection
  • Shared view guard: no collection when viewing ?t= URLs
  • Tutorial positioning verified via Playwright screenshots (steps 6, 11, 12, 13, 15)

🤖 Generated with Claude Code

jeremymanning and others added 10 commits March 21, 2026 10:03
… toggle

- GoatCounter snippet in index.html for cookie-free visitor analytics
- Response collection module (src/collection/collector.js): encodes tokens
  every N responses and POSTs to configurable GAS endpoint (fire-and-forget)
- Tutorial consent step: "Contribute to science!" modal with opt-in/out buttons
- About modal: data collection disclosure text and opt-out toggle switch
- Offline decoder script (scripts/decode-tokens.js) for token→CSV/JSON
- Feature-flagged: collection disabled when ENDPOINT_URL is empty

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pass aggregatedQuestions (2500) to maybeCollect instead of questionIndex Map
- buildIndex() now called inside collector for proper token encoding
- renderMarkdownLite supports <i> and <em> tags (consent step HTML)
- Move info icon to after "about button" in consent text
- Verified: 10-question token round-trips with 100% fidelity via GAS endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Domain column removed from GAS payload (redundant — decoder derives
  per-question domain from question_ids)
- Added navigator.sendBeacon on beforeunload to capture partial sessions
  that don't reach the N-response threshold
- Track _lastSentCount to avoid duplicate sends on re-answers
- Decoder now outputs domain derived from each question's domain_ids

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sendBeacon with JSON body fails silently on GAS endpoints (302 redirect).
fetch with keepalive:true survives page unload and follows redirects.
Verified: both interval (10 responses) and beforeunload (11 responses)
rows appear in Google Sheet with correct tokens.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collection is now OFF by default. Users opt in via the tutorial
consent step ("I'd like to help!") or the About modal toggle.
Updated HTML toggle initial state to match (aria-checked=false, thumb left).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Deep Dive and Exploring Domain-Specific steps: positionHint left→right
  so modal appears next to the quiz panel
- Header button highlights (trophy, suggest, share): add overflow:visible
  rule for .header-right/.header-actions when child has tutorial-highlight,
  so the box-shadow page-dim effect isn't clipped by overflow-x:scroll

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use positionHint 'quiz-final' instead of 'right' so the tutorial modal
appears to the left of the quiz panel without overlapping it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setCollectionEnabled now dispatches a 'collect-pref-change' custom event.
The About modal listens for it and updates the toggle in real-time, so
opting in via the tutorial consent step immediately reflects in an
already-open About modal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace PsyArXiv preprint link with published Nature Communications
citation (Fitzpatrick, Heusser, Manning 2026) in both the About modal
and the landing page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jeremymanning jeremymanning merged commit b9736f6 into main Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant