# 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:///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:///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`.