Vue Storefront is now Alokai! Learn More
Analytics

Analytics

Analytics is especially important for the Coveo. The AI Model learns how to give more accurate results based on it. Components shipped by us, already leverage Interactive Results and Interactive Instant Results on click but there is much more.

A lot of events are already supported by the Coveo Headless library, for example, if you send a query request, or select facet, change the sort order and so on. Basically, every interaction with a headless controller will be automatically tracked. However, there are some events that are not supported out of the box, like view product details page, add to cart, remove from cart, etc.

Currently supported events:

  • all native headless events
  • click on the interactive result (search result)
  • click on the interactive instant result (quick search result)
  • view product details page

Table of contents

Our module doesn't support each kind of events supported by Coveo yet.

Including coveoua library

To handle custom events, there is a coveoua library that can be used. Our composable useCoveoua bases on the coveoua library that has to be added to the head. You can do that by modifying nuxt.config.js file, and adding inside app.head object:

Next
// `useCoveoua` hook loads the `coveoua` library automatically. You do not need to include it manually.

Capturing customer events

To reach better personalization you need to attach anonymous identifier to authenticated customer:

Next
// layouts/DefaultLayout.tsx
// Import following
import { CoveoSearch, useCoveoCustomer } from '@sf-modules/coveo'; import { useEffect } from 'react'; 
export function DefaultLayout({ children, breadcrumbs = [] }: LayoutPropsType): JSX.Element {
  // ...
  // Add the following
  const { buildIdentifier } = useCoveoCustomer();   useEffect(() => {     buildIdentifier(customer.data);   }, [customer]);   // ...
}

You need to perform same operations in different layouts related to Customer's session. In base template, these will be following:

Next
// layouts/CheckoutLayout.tsx
// Import following
import { useCoveoCustomer } from '@sf-modules/coveo'; import { useEffect } from 'react'; import { useCart, useCustomer } from '~/hooks'; 
export function CheckoutLayout({ children, breadcrumbs = [] }: LayoutPropsType): JSX.Element {
  // ...
  // Add the following
  const customer = useCustomer();   const { buildIdentifier } = useCoveoCustomer();   useEffect(() => {     buildIdentifier(customer.data);   }, [customer]);   // ...
}
Next
// layouts/OrderLayout.tsx
// Import following
import { useEffect } from 'react'; import { useCoveoCustomer } from '@sf-modules/coveo'; import { useCustomer } from '~/hooks'; 
export function OrderLayout({ children, breadcrumbs = [] }: LayoutPropsType): JSX.Element {
  // ...
  // Add the following
  const customer = useCustomer();   const { buildIdentifier } = useCoveoCustomer();   useEffect(() => {     buildIdentifier(customer.data);   }, [customer]);   // ...
}

Analytics mappers

To facilitate working with Coveoua and our ecosystem, we provide mappers transforming data types to required by analytics CoveouaProduct type. Thanks to that, you could easily send events using data from different sources like:

Using existing mappers

Using mapGetProductDetailsToCoveouaProduct mapper that has the following signature:

(source: GetProductDetailsData): CoveouaProduct
import { mapGetProductDetailsToCoveouaProduct, useCoveoua } from '@sf-modules/coveo';

//...
const coveoua = useCoveoua();
const productDetail = useProductDetails({ id, sku });

useEffect(() => {
  coveoua.sendViewPDP(productDetail.data!)); // TS Error: Invalid type!
  coveoua.sendViewPDP(mapGetProductDetailsToCoveouaProduct(productDetail.data!)); // It works
});
//...

Using mapNormalizedResultToCoveouaProduct mapper that has the following signature:

(result: NormalizedResult): CoveouaProduct
import { mapNormalizedResultToCoveouaProduct, useCoveoua } from '@sf-modules/coveo';

//...
const coveoua = useCoveoua();
const productCatalog = useCoveoSearchProducts();

// inside some callback
const someProduct = productsCatalog.products[0].result;
coveoua.sendViewPDP(someProduct!)); // TS Error: Invalid type!
coveoua.sendViewPDP(mapNormalizedResultToCoveouaProduct(someProduct!)); // It works
//...

Creating own analytics mapper

What if your data type doens't match any aforementioned mappers? In that case, it is recommended to create own mapper. It is as simple as creating function, all it has to do is return CoveouaProduct type.

import { CoveouaProduct } from '@sf-modules/coveo';

interface MyType {
  a: number;
  b: string;
  // ...
}

export function mapMyTypeToCoveouaProduct(result: MyType): CoveouaProduct {
  return {
    // ...
  }
}

Usage:

import { useCoveoua } from '@sf-modules/coveo';
import { mapMyTypeToCoveouaProduct } from '...';

//...
const coveoua = useCoveoua();

// In some function/composable/hook:
const myData: MyType = // ...
coveoua.sendViewPDP(myData!); // TS Error: Invalid type!
coveoua.sendViewPDP(mapMyTypeToCoveouaProduct(myData!)); // It works
//...

Events

Below, we specify every supported custom events with a brief description of how to implement it in your application.

Available events:

View PDP event

This event should be sent when a user views a product details page. It is important for the Coveo AI Model to learn more about the user's behavior. To track this event, you can use the sendViewPDP method from the useCoveoua composable.

// pages/product/[slug]/[id].tsx
import { useCoveoua, mapGetProductDetailsToCoveouaProduct } from '@sf-modules/coveo';
import { useEffect } from 'react';

const productDetail = useProductDetails({ id, sku: getQueryParameter(sku) });
const coveoua = useCoveoua();

useEffect(() => {
  coveoua.sendViewPDP(mapGetProductDetailsToCoveouaProduct(productDetail.data!));
});

Cart events

This section assumes, you already implemented View PDP event.

There are 3 places where you can modify the cart in our application:

  • PDP,
  • PLP (Category/Search results),
  • Cart view.

We need to trigger events in all of them.

PDP

// components/AddToCartButton/AddToCartButton.tsx
import { useCoveouaCart } from '@sf-modules/coveo';
// ...
const { sendAddToCartEvent } = useCoveouaCart();
// ...
return (
  <SfButton
    // onClick={() => addToCart.mutate({ quantity })}
    onClick={() => {
      addToCart.mutate({ quantity });
      sendAddToCartEvent({ productId, quantity: quantity || 1, sku: sku as string | undefined });
    }}
    // ...
// ...

PLP

It's covered by us changes made in previous step.

Cart view

// components/ui/CartProductCardWrapper/CartProductCardWrapper.tsx
import { useCoveouaCart } from '@sf-modules/coveo';
// ...
// const { id } = props;
const { id, value, sku } = props;
const { sendUpdateCartEvent, sendRemoveFromCartEvent } = useCoveouaCart();

return (
  <CartProductCard
    {...props}
    onUpdate={(quantity) => {
      updateCartQuantity.mutate({ quantity });
      sendUpdateCartEvent({ productId: id, oldQuantity: value, quantity, sku }); // +
      removeWantedQuantities(id);
    }}
    onRemove={() => {
      removeCartLineItem.mutate();
      sendRemoveFromCartEvent({ productId: id, quantity: value, sku }); // +
      removeWantedQuantities(id);
    }}
// ...

Purchase event

The sendPurchase method should be called just after user placed an order.

import { useCoveoua } from '@sf-modules/coveo';

const coveoua = useCoveoua();

const onPlacedOrderSuccesfully = (order: SfOrder) => {
  coveoua.sendPurchase(order!);
};