Google analytics integration.

Sentry.io integration.
Vince integration.
Region-scoped consent (denied-by-default in EU/EEA/UK/CH, granted elsewhere, DPDP-friendly notice)
This commit is contained in:
2026-09-06 16:03:13 +00:00
parent 43c6fbe437
commit e0eb577e3e
37 changed files with 2059 additions and 274 deletions
+106
View File
@@ -0,0 +1,106 @@
# Analytics Setup — mozimo.in
The site ships with a layered, code-complete analytics stack. This document
covers the dashboards you must create and the steps only you can do; after
them, everything works with no further code changes.
```
Visitor browser
├─ gtag.js (GA4) ───────────────► GA4 property (consent-mode v2 gated)
├─ /stats/js/script.js ─┐
└─ events /stats/api/event ── Caddy ──► Vince container (first-party)
Server (SSR) ── errors ──────────► Sentry
Shopify (shop.mozimo.in) ─ purchase/checkout ──► same GA4 property
```
## 1. What is already wired in code
| Layer | Implementation |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GA4 | Direct `gtag.js` injected in `src/app/__root.tsx` head scripts (production only, when `VITE_GA4_MEASUREMENT_ID` is set at build). `send_page_view: false` + `PageViewTracker` = exactly one `page_view` per navigation. Cross-domain linker to `shop.mozimo.in`. |
| Consent | Google Consent Mode v2: denied-by-default in EU/EEA/UK/CH, granted elsewhere. `ConsentBanner` shows only to those visitors (timezone-detected, cookie-stored choice, `/privacy` explains everything). |
| First-party layer | Vince (Plausible protocol), self-hosted via `ansible/roles/analytics`, proxied same-origin at `/stats/*` by Caddy — survives ad blockers, needs no consent. |
| E-commerce events | `view_item_list`, `select_item`, `view_item`, `begin_checkout`, `search`, `generate_lead` (forms — no PII), `click`/`contact` (all outbound/tel/mailto links via `src/components/Link.tsx`), `video_start`, `page_not_found`, `web_vitals`. **`purchase` is NOT hand-coded** — Shopify reports it (step 2.2). |
| Errors | Sentry client + server (custom entries `src/client.tsx` / `src/server.ts`), off unless DSN set. |
## 2. One-time dashboards (manual)
### 2.1 GA4 property
1. analytics.google.com → Admin → **Create property** (`mozimo.in`),
currency **INR**, timezone **India**.
2. Add a **Web** data stream for `https://mozimo.in` → copy the
Measurement ID (`G-…`).
3. Put it in your local `.env` as `VITE_GA4_MEASUREMENT_ID=G-…`
(it is baked into the bundle at `docker build` by the Makefile).
4. Data retention: Admin → Data settings → set **14 months**.
5. Data filters: add your office IP as **Internal Traffic** (defined filter).
6. Cross-domain: Data stream → Configure tag settings → **Configure your
domains** → add `shop.mozimo.in`.
7. Mark **key events**: `generate_lead`, `begin_checkout` (Admin → Events).
8. Register **custom definitions** (dimensions): `web_vitals_metric`,
`web_vitals_rating`, `lead_type`, `lead_topic`, `lead_budget`,
`search_term` (dimension), `result_count`, `link_domain`, `video_title`.
9. Admin → Product links → **Search Console** → link the existing property
(site verification meta is already in `__root.tsx`).
### 2.2 Shopify → GA4 (this is what makes purchases visible)
1. Shopify Admin → Settings → Apps → **Google & YouTube** channel → install.
2. Connect the same Google account and select the GA4 property from 2.1.
3. Shopify then injects the Google tag on `shop.mozimo.in` (storefront +
checkout) and reports `purchase`/`begin_checkout`/`add_to_cart` into the
property. The cross-domain linker on both sides stitches sessions —
verify a `_gl=` parameter appears when clicking _Order this_ on a
product page.
4. Place one real (then refunded) test order and confirm `purchase` in
GA4 Realtime/DebugView.
### 2.3 Sentry
1. sentry.io → create project **mozimo-web** (platform: TanStack Start /
JavaScript).
2. Copy the DSN into your local `.env` (`VITE_SENTRY_DSN`) **and** into
`ansible/files/.env` (`SENTRY_DSN`) — client needs it at build, server at
runtime.
3. Optional sourcemap upload (better stack traces): put `SENTRY_AUTH_TOKEN`,
`SENTRY_ORG`, `SENTRY_PROJECT` in the build machine env. Builds without
them skip Sentry's vite plugin entirely.
4. Verify: visit any page with `?sentry-test=1`… simplest check is a thrown
error in devtools; a visible page_view + the event landing in Sentry is
enough for sign-off.
### 2.4 Vince (first-party layer)
1. `./deploy.sh` once (the playbook creates the container + Caddy route).
2. Open `https://<www_host>/stats/` → register the admin account (first
visit only). Then **Add a website** with domain `mozimo.in`.
3. Set `VITE_PLAUSIBLE_DOMAIN=mozimo.in` in your local `.env` and redeploy —
the script tag is injected only when this is set.
4. Check `https://<www_host>/stats/js/script.js` returns the script (200)
and that browsing the site populates the dashboard within seconds.
5. With uBlock Origin enabled: visits still appear in Vince but not GA4 —
expected and exactly why this layer exists.
## 3. Verifying GA4 events (DebugView)
Append `?debug_mode=1`… simpler: build with `VITE_GA4_DEBUG=1` in `.env`
temporarily — every event then carries `debug_mode: true` and appears in
GA4 Admin → DebugView (with the Tag Assistant Companion extension).
Walk-through: home (page_view, view_item_list _The Mozimo Edit_) →
/chocolate → /collections/bars (view_item_list) → a product (view_item) →
_Order this_ (begin_checkout + outbound click with `_gl`) → /contact
submit (generate_lead) → search overlay (`search`).
## 4. Operational notes
- **Dev is always excluded** — analytics scripts only inject in production
builds.
- **Disabling a tool** = leaving its env var empty. Nothing breaks.
- **Changing the GA4 property** = change the env var and redeploy.
- Sitemap (`dist/client/sitemap.xml` from `scripts/generate-sitemap.mjs`)
and `robots.txt` are live for Search Console; submit the sitemap once in
Search Console → Sitemaps.
- Phase 2 (in-container cart) should add `add_to_cart`, `view_cart`,
`remove_from_cart` via the same helpers in `src/lib/analytics.ts`.