Skip to main content

Overview

The Tagging module handles analytics tracking and event tagging for user interactions. It manages user consent, session tracking, query tagging, and integration with analytics platforms for search behavior analysis.

State

The Tagging module maintains the following state:
User consent status for tracking (null = not set, true = granted, false = denied)
noResultsTaggingEnabled
boolean
Whether no-results events should be tracked
queryTaggingInfo
TaggingRequest | null
Current query tagging information from search response
toolingTaggingInfo
ToolingTaggingInfo
Tracking info for tooling events (display, add to cart)
config
TaggingConfig
Module configuration options

Configuration

sessionTTLMs
number
default:"1800000"
Session time-to-live in milliseconds (default: 30 minutes)
queryTaggingDebounceMs
number
default:"2000"
Debounce time before sending query tagging events
storageKey
string | null
default:"null"
Key for storing tagging data in browser storage
storageTTLMs
number | null
default:"null"
Time-to-live for stored tagging data

Getters

The Tagging module has no getters (uses direct state access).

Mutations

Set user tracking consent
setQueryTaggingInfo
(state, info: TaggingRequest) => void
Update query tagging information
setNoResultsTaggingEnabled
(state, module: string) => void
Enable no-results tagging for semantic queries or related prompts
setConfig
(state, config: Partial<TaggingConfig>) => void
Update configuration
mergeConfig
(state, config: Partial<TaggingConfig>) => void
Merge configuration with existing config

Actions

track
(context, event: TaggingEvent) => void
Track an analytics event

Events Tracked

Common events you can track:
UserAcceptedAQuery
{ query: string }
User submitted a search query
UserClickedAResult
{ result: Result }
User clicked a search result
UserClickedARecommendation
{ recommendation: Result }
User clicked a recommendation
UserClickedAFilter
{ filter: Filter }
User selected/deselected a filter
UserClickedAHistoryQuery
{ query: string }
User clicked a history query
UserAddedToCart
{ result: Result }
User added a product to cart
UserDisplayedResults
{ results: Result[] }
Results were displayed to user
NoResultsDisplayed
{ query: string }
No results were found for query

Usage Examples

import { useStore } from 'vuex'

const store = useStore()

// Request and set user consent
const userConsent = await requestTrackingConsent()
store.commit('x/tagging/setConsent', userConsent)

// Check consent before tracking
const hasConsent = store.state.x.tagging.consent
if (hasConsent) {
  // Track events
}

Track Search Query

// Track when user submits a query
store.dispatch('x/tagging/track', {
  event: 'UserAcceptedAQuery',
  query: 'running shoes',
  timestamp: Date.now()
})

Track Result Click

import type { Result } from '@empathyco/x-types'

const result: Result = {
  id: 'product-123',
  name: 'Running Shoes',
  url: '/products/running-shoes',
  // ... other properties
}

store.dispatch('x/tagging/track', {
  event: 'UserClickedAResult',
  result,
  position: 3, // Position in results list
  query: store.state.x.search.query
})

Track Filter Interactions

import type { Filter } from '@empathyco/x-types'

const filter: Filter = {
  id: 'brand:nike',
  facetId: 'brand',
  label: 'Nike',
  selected: true,
  modelName: 'SimpleFilter'
}

store.dispatch('x/tagging/track', {
  event: 'UserClickedAFilter',
  filter,
  action: filter.selected ? 'select' : 'deselect'
})

Track Add to Cart

store.dispatch('x/tagging/track', {
  event: 'UserAddedToCart',
  result: product,
  quantity: 1,
  query: store.state.x.search.query
})

Component Integration

<template>
  <div class="result-card" @click="handleClick">
    <img :src="result.images[0]" :alt="result.name" />
    <h3>{{ result.name }}</h3>
    <p>{{ result.price?.value }}</p>
    <button @click.stop="handleAddToCart">Add to Cart</button>
  </div>
</template>

<script setup lang="ts">
import { useStore } from 'vuex'
import type { Result } from '@empathyco/x-types'

interface Props {
  result: Result
  position: number
}

const props = defineProps<Props>()
const store = useStore()

const handleClick = () => {
  // Track click
  store.dispatch('x/tagging/track', {
    event: 'UserClickedAResult',
    result: props.result,
    position: props.position,
    query: store.state.x.search.query
  })
  
  // Navigate to product
  window.location.href = props.result.url
}

const handleAddToCart = () => {
  // Track add to cart
  store.dispatch('x/tagging/track', {
    event: 'UserAddedToCart',
    result: props.result,
    query: store.state.x.search.query
  })
  
  // Add to cart logic
  store.dispatch('cart/addItem', props.result)
}
</script>

Track Display Events

<template>
  <div>
    <div 
      v-for="(result, index) in visibleResults" 
      :key="result.id"
      v-observe-visibility="(isVisible) => onVisibilityChange(result, index, isVisible)"
    >
      <ResultCard :result="result" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useStore } from 'vuex'
import type { Result } from '@empathyco/x-types'

const store = useStore()
const displayedResults = ref<Set<string>>(new Set())

const onVisibilityChange = (
  result: Result, 
  position: number, 
  isVisible: boolean
) => {
  if (isVisible && !displayedResults.value.has(result.id)) {
    displayedResults.value.add(result.id)
    
    store.dispatch('x/tagging/track', {
      event: 'UserDisplayedResult',
      result,
      position,
      query: store.state.x.search.query
    })
  }
}
</script>

Query Tagging Integration

import { watch } from 'vue'

// Update query tagging info from search response
watch(
  () => store.state.x.search.queryTagging,
  (queryTagging) => {
    if (queryTagging) {
      store.commit('x/tagging/setQueryTaggingInfo', queryTagging)
    }
  }
)

// Access query tagging for tracking requests
const queryTaggingInfo = store.state.x.tagging.queryTaggingInfo
if (queryTaggingInfo) {
  // Include in tracking request
  const trackingData = {
    event: 'UserClickedAResult',
    result,
    tagging: queryTaggingInfo
  }
}

No Results Tracking

import { watch } from 'vue'

// Track when no results are displayed
watch(
  () => store.state.x.search.isNoResults,
  (isNoResults) => {
    if (isNoResults) {
      const query = store.state.x.search.query
      
      store.dispatch('x/tagging/track', {
        event: 'NoResultsDisplayed',
        query,
        filters: store.getters['x/facets/selectedFilters']
      })
    }
  }
)

Session Tracking

// Configure session tracking
store.commit('x/tagging/setConfig', {
  sessionTTLMs: 30 * 60 * 1000, // 30 minutes
  storageKey: 'x-tagging-session',
  storageTTLMs: 24 * 60 * 60 * 1000 // 24 hours
})

// Session info is automatically included in events

Custom Event Tracking

// Track custom events
store.dispatch('x/tagging/track', {
  event: 'CustomEvent',
  category: 'user_interaction',
  action: 'filter_refinement',
  label: 'price_range',
  value: { min: 0, max: 100 },
  metadata: {
    page: 'search_results',
    deviceType: 'mobile'
  }
})
<template>
  <div v-if="showConsentBanner" class="consent-banner">
    <p>We use cookies and tracking to improve your experience.</p>
    <button @click="acceptConsent">Accept</button>
    <button @click="denyConsent">Decline</button>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { useStore } from 'vuex'

const store = useStore()
const showConsentBanner = ref(false)

const consent = computed(() => store.state.x.tagging.consent)

onMounted(() => {
  // Show banner if consent not set
  if (consent.value === null) {
    showConsentBanner.value = true
  }
})

const acceptConsent = () => {
  store.commit('x/tagging/setConsent', true)
  showConsentBanner.value = false
  localStorage.setItem('tracking-consent', 'true')
}

const denyConsent = () => {
  store.commit('x/tagging/setConsent', false)
  showConsentBanner.value = false
  localStorage.setItem('tracking-consent', 'false')
}
</script>

Integration with Analytics Platforms

Google Analytics

import { watch } from 'vue'

// Forward tagging events to Google Analytics
watch(
  () => store.state.x.tagging,
  () => {
    // Intercept track action or use event bus
  },
  { deep: true }
)

function sendToGA(event: TaggingEvent) {
  if (window.gtag) {
    window.gtag('event', event.event, {
      ...event,
      send_to: 'GA_MEASUREMENT_ID'
    })
  }
}

Custom Analytics

import { watch } from 'vue'
import { useStore } from 'vuex'

const store = useStore()

// Listen to tagging events and forward to your analytics service
store.subscribeAction({
  after: (action, state) => {
    if (action.type === 'x/tagging/track') {
      sendToAnalytics(action.payload)
    }
  }
})

async function sendToAnalytics(event: TaggingEvent) {
  if (!store.state.x.tagging.consent) return
  
  await fetch('/api/analytics', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      event,
      sessionId: getSessionId(),
      timestamp: Date.now()
    })
  })
}

Type Definitions

TaggingRequest

interface TaggingRequest {
  url: string
  params: Record<string, any>
}

TaggingEvent

interface TaggingEvent {
  event: string
  [key: string]: any
}

Type Reference

Source: /home/daytona/workspace/source/packages/x-components/src/x-modules/tagging/store/module.ts:1

Resources

x-types

Tagging type definitions

Tagging Documentation

Tagging implementation guide

Build docs developers (and LLMs) love