# 0052. Analytics events are declared by the views and pushed onto the Tag Manager data layer

- **Status:** Accepted
- **Date:** 2026-09-24

## Context

Page views alone cannot say which pages and searches bring enquiries. The live container (`GTM-NB2L7B6`) fired
its GA4 and seven Google Ads conversion tags from triggers bound to the old Drupal markup — element visibility,
generic form submits, `#edit-continue` — which go silent on the new site. The lead forms do not end alike: the
sidebar's three forms and the storage page's callback post over AJAX and always answer 200; the contact form and
the proposal request page re-render on success.

## Decision

Molino's layer (its ADR 0060): **a view declares what to measure, one module sends it.** `AnalyticsHelper`
renders `data-ga-event` (an outcome only the server knows), `data-ga-form`, `data-ga-source`, and — new here —
`data-ga-storage` / `data-ga-location`. AJAX outcomes arrive as window events (`form:success`, `sidebar:open`,
`compare:added`, `storages:filtered`); `Analytics.js` alone knows the event vocabulary.

Unlike Molino, the transport is **`dataLayer.push( { event, …params } )`**, not `gtag()`: the container turns the
events into GA4 events and Ads conversions with Custom Event triggers (`docs/analytics/`), which no markup change
can break. One `generate_lead` with a `form_type`, not one event per form.

There is no "add to offer list" on the frontend (ADR 0024), so the intent signal is `sidebar_open`. Storage
search filters are measured as slugs. Outside production events go to the console (`APP_CONFIG.debug`).

## Alternatives

- **`gtag( 'event' )` as on Molino.** Works for GA4 through the container's Google tag, but the Ads conversions
  still need triggers, and those are Custom Event triggers on the data layer either way.
- **DOM triggers in the container.** What broke: they re-derive the form's identity from selectors.
- **Measuring the submit instead of the outcome.** Would count validation failures as leads.

## Consequences

- A new AJAX lead form has to dispatch `form:success`, and its form carry `data-ga-form`.
- Custom parameters show in GA4 reports only once registered as custom dimensions, not backfilled.
- The container needs a one-off re-wiring of the Ads conversions — `docs/analytics/README.md`.
- The Tag Manager loader lives in `Settings::embedHead`, per environment, and stays **empty on dev**: the
  container's own GA4 tag would otherwise send dev page views to the live property.
