Skip to content

Commit

Permalink
feat: handle RC Events (#2567)
Browse files Browse the repository at this point in the history
## What's the purpose of this pull request?

Add VTEX RC Events to FastStore.
- HomeView
- CategoryView
- DepartmentView
- InternalSiteSearchView
- ProductView
- OtherView

## How it works?

The events weren't being sent to RC using the arguments expected by RC,
so now the new `handleEvent` function does that - depending on the type
of the page, it'll send the correct page view event to RC.

## How to test it?

Run `yarn dev` on this branch and watch the Network tab for the `v8`
request. I'll add some screenshots below to demonstrate:

| Event | Screenshot |
| --- | --- |
| homeView | <img width="1438" alt="homeView"
src="https://github.com/user-attachments/assets/36006b36-226f-4742-b166-54b8120d2d07"
/> |
| productView | <img width="1435" alt="productView"
src="https://github.com/user-attachments/assets/27be74bc-2595-4da1-b72c-f6e5254def10"
/> |
| categoryView | <img width="1437" alt="categoryView"
src="https://github.com/user-attachments/assets/a6e415c5-05af-4fe8-b3d3-7b4961121a73"
/> |
| departmentView | <img width="1438" alt="departmentView"
src="https://github.com/user-attachments/assets/a560f4fc-48e7-43aa-aaf1-ba400cb002c1"
/> |
| internalSiteSearchView | <img width="1438"
alt="internalSiteSearchView"
src="https://github.com/user-attachments/assets/3086274a-559a-434f-93c8-1baaa85c5284"
/> |
| otherView | <img width="1434" alt="otherView"
src="https://github.com/user-attachments/assets/b70c91cf-7339-463a-a19a-80507b9c22ce"
/> |

### Starters Deploy Preview

https://starter-5wjtxcga4-vtex.vercel.app/ /
https://sfj-f304385--starter.preview.vtex.app/
([PR](vtex-sites/starter.store#637))

## References

- [Jira task](https://vtex-dev.atlassian.net/browse/SFS-1796)
- [Slack
thread](https://vtex.slack.com/archives/C051B6LL91U/p1730123029648129)
- [RC app](https://github.com/vtex/request-capture-app)
- [RC script](https://github.com/vtex/request-capture-script)
  • Loading branch information
lariciamota authored Dec 19, 2024
1 parent 950a381 commit 486563d
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 19 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { type PropsWithChildren } from 'react'
import type { PropsWithChildren, ReactElement } from 'react'

import { usePageViewEvent } from './sdk/analytics/hooks/usePageViewEvent'

function Layout({ children }: PropsWithChildren) {
usePageViewEvent()
usePageViewEvent((children as ReactElement)?.props)

return <>{children}</>
}
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/sdk/analytics/hooks/usePageViewEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { PageViewEvent } from '@faststore/sdk'
import { useRouter } from 'next/router'
import { useCallback, useEffect } from 'react'

export const usePageViewEvent = () => {
export const usePageViewEvent = (props?: any) => {
const sendPageViewEvent = useCallback(() => {
import('@faststore/sdk').then(({ sendAnalyticsEvent }) => {
sendAnalyticsEvent<PageViewEvent>({
Expand All @@ -11,10 +11,11 @@ export const usePageViewEvent = () => {
page_title: document.title,
page_location: location.href,
send_page_view: true,
...props,
},
})
})
}, [])
}, [props])

const router = useRouter()

Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/sdk/analytics/platform/vtex/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import type { AnalyticsEvent } from '@faststore/sdk'

import handleRCEvent from './rc'
import handleSearchEvent from './search'

export default function sendEvent(event: AnalyticsEvent) {
// VTEX RC
window?.sendrc?.(event.name, event.params)
handleRCEvent(event)

// VTEX Intelligent Search
handleSearchEvent(event)
Expand Down
128 changes: 128 additions & 0 deletions packages/core/src/sdk/analytics/platform/vtex/rc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { AnalyticsEvent, PageViewEvent } from '@faststore/sdk'
import { ServerProductQueryQuery } from '@generated/graphql'
import {
CategoryView,
DepartmentView,
HomeView,
IntelligentSearchQueryEvent,
InternalSiteSearchView,
OtherView,
ProductView,
SearchEvents,
} from '../../types'

const EventNames = {
OTHER_VIEW: 'otherView',
HOME_VIEW: 'homeView',
CATEGORY_VIEW: 'categoryView',
DEPARTMENT_VIEW: 'departmentView',
INTERNAL_SITE_SEARCH_VIEW: 'internalSiteSearchView',
PRODUCT_VIEW: 'productView',
} as const

type RequestCaptureEventNames = (typeof EventNames)[keyof typeof EventNames]
type RequestCaptureEvent =
| HomeView
| CategoryView
| DepartmentView
| InternalSiteSearchView
| ProductView
| OtherView

const sendEvent = (
eventName: RequestCaptureEventNames,
eventParams: RequestCaptureEvent
) => {
window?.sendrc?.(eventName, eventParams)
}

const handleEvent = (event: AnalyticsEvent | SearchEvents) => {
let eventParams
switch (event.name) {
case 'page_view':
eventParams = (event as PageViewEvent).params
const pageType = eventParams?.page?.type ?? eventParams?.type

switch (pageType) {
case 'plp':
if (!eventParams?.page_location?.includes('fuzzy')) {
// Skip when there is no fuzzy parameters on the URL,
// otherwise it'll be sent twice (once without fuzzy and once with fuzzy)
break
}
const collection = eventParams?.data?.collection
const collectionCategoryList =
collection?.breadcrumbList?.itemListElement
if (collectionCategoryList.length > 1) {
sendEvent(EventNames.CATEGORY_VIEW, {
departmentName: collectionCategoryList[0]?.name,
categoryId: collection?.id,
categoryName:
collectionCategoryList[collectionCategoryList.length - 1]?.name,
})
} else {
sendEvent(EventNames.DEPARTMENT_VIEW, {
departmentId: collection?.id,
departmentName: collectionCategoryList[0]?.name,
})
}

break

case 'pdp':
const product = eventParams?.data
?.product as ServerProductQueryQuery['product']
const productCategoryList = product?.breadcrumbList?.itemListElement
const offers = product?.offers?.offers
const sellerIds = offers.map((offer) => offer.seller.identifier)
const skusOutOfStock = offers.filter(
(offer) => offer.availability === 'https://schema.org/OutOfStock'
)

sendEvent(EventNames.PRODUCT_VIEW, {
skuStockOutFromProductDetail: skusOutOfStock,
productId: product?.isVariantOf?.productGroupID,
productName: product?.isVariantOf?.name,
productBrandName: product?.brand?.name,
productDepartmentName: productCategoryList
? productCategoryList[0]?.name
: '',
productCategoryName:
productCategoryList.length > 1
? productCategoryList[productCategoryList.length - 2]?.name
: '',
productPrice: offers[0]?.price,
productListPrice: offers[0]?.listPrice,
sellerId: offers[0]?.seller?.identifier,
sellerIds: sellerIds.join(','),
})
break

case 'home':
sendEvent(EventNames.HOME_VIEW, {})
break

case 'search':
// This one is skipped because the event related to search view is being
// sent using the intelligent_search_query event due to its parameters
break

default:
sendEvent(EventNames.OTHER_VIEW, {})
}
break

case 'intelligent_search_query':
eventParams = (event as IntelligentSearchQueryEvent).params
sendEvent(EventNames.INTERNAL_SITE_SEARCH_VIEW, {
siteSearchTerm: eventParams.term,
siteSearchForm: eventParams.url,
siteSearchResults: eventParams.totalCount,
})
break

default:
}
}

export default handleEvent
16 changes: 2 additions & 14 deletions packages/core/src/sdk/analytics/platform/vtex/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
* More info at: https://developers.vtex.com/docs/api-reference/intelligent-search-events-api-headless
*/
import type { AnalyticsEvent } from '@faststore/sdk'
import type {
IntelligentSearchAutocompleteClickEvent,
IntelligentSearchAutocompleteQueryEvent,
IntelligentSearchQueryEvent,
SearchSelectItemEvent,
} from '../../types'
import type { SearchEvents } from '../../types'

import config from '../../../../../discovery.config'
import { getCookie } from '../../../../utils/getCookie'
Expand Down Expand Up @@ -103,14 +98,7 @@ const isFullTextSearch = (url: URL) =>
typeof url.searchParams.get('q') === 'string' &&
/^\/s(\/)?$/g.test(url.pathname)

const handleEvent = (
event:
| AnalyticsEvent
| SearchSelectItemEvent
| IntelligentSearchQueryEvent
| IntelligentSearchAutocompleteQueryEvent
| IntelligentSearchAutocompleteClickEvent
) => {
const handleEvent = (event: AnalyticsEvent | SearchEvents) => {
switch (event.name) {
case 'search_select_item': {
const url = new URL(event.params.url)
Expand Down
55 changes: 55 additions & 0 deletions packages/core/src/sdk/analytics/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,58 @@ export interface IntelligentSearchAutocompleteClickEvent {
name: 'intelligent_search_autocomplete_click'
params: IntelligentSearchAutocompleteClickParams
}

export type SearchEvents =
| SearchSelectItemEvent
| IntelligentSearchQueryEvent
| IntelligentSearchAutocompleteQueryEvent
| IntelligentSearchAutocompleteClickEvent

/**
* RC event types
* Types copied from Request Capture App: https://github.com/vtex/request-capture-app/blob/1becac32c002cb03a57bf36c8a7f9400eab8b933/react/typings/rcevents.d.ts
*/

export interface HomeView {}

export interface CategoryView {
departmentId?: string
departmentName?: string
categoryId?: string
categoryName?: string
}

export interface DepartmentView {
departmentId?: string
departmentName?: string
}

export interface InternalSiteSearchView {
siteSearchTerm?: string // e.g.: "areia"
siteSearchForm?: string // e.g.: "/gatos/ambiente--gatos/caixa-de-areia/areia?PS=20"
siteSearchCategory?: string // e.g.: "10000283"
siteSearchResults?: number // e.g.: 26
}

type SkuId = string

export interface ProductView {
skuStockOutFromProductDetail: string[]
productId: string
productReferenceId: string
productEans: string[]
skuStocks: Record<SkuId, number>
productName: string
productBrandId: string
productBrandName: string
productDepartmentId: string
productDepartmentName: string
productCategoryId: string
productCategoryName: string
productListPrice: number
productPrice: number
sellerId: string
sellerIds: string // e.g.: "00443713,04412311,1"
}

export interface OtherView {}
1 change: 1 addition & 0 deletions packages/sdk/src/analytics/events/page_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export interface PageViewParams {
page_title?: string,
page_location?: string,
send_page_view?: boolean,
[key: string]: any
}

export interface PageViewEvent {
Expand Down

0 comments on commit 486563d

Please sign in to comment.