diff --git a/README.md b/README.md
index 469e8c2..b07d1af 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,46 @@ Find out more about Managed Components [here](https://blog.cloudflare.com/zaraz-
`tid` Pixel Tag ID - The Pinterest Tag ID is the unique identifier of your Pinterest tag. [Learn more](https://help.pinterest.com/en/business/article/track-conversions-with-pinterest-tag).
+---
+
+## Fields Description
+
+Fields are properties that can/must be sent with certain events.
+
+### Required Fields
+
+#### Event Name `string` _required_
+
+`event` - The name of the tracking event to be sent to Pinterest.
+
+### Optional Fields
+
+#### User Defined Event `string` _optional_
+
+`ude` - Specify a custom event name for audience targeting purposes. Spaces in the event name will be removed. [Learn more](https://help.pinterest.com/en/business/article/add-event-codes).
+
+#### Partner Data Email `string` _optional_
+
+`pdem` - Specifies the email address associated with the partner data, if applicable.
+
+#### Tag Manager `string` _optional_
+
+`tm` - Indicates the Tag Manager used, defaults to 'pinterest-mc' if not specified.
+
+#### Lead Type `string` _optional_
+
+`lead_type` - Describes the type of lead being tracked, such as 'Newsletter', 'Signup', etc.
+
+#### Video Title `string` _optional_
+
+`video_title` - The title of the video for tracking specific video events.
+
+#### E-commerce Tracking `boolean` _optional_
+
+`ecommerce` - Enables or disables the forwarding of Zaraz E-commerce API events to Pinterest. This includes events like Search, AddToCart, and Checkout. [Learn more](https://help.pinterest.com/en-gb/business/article/add-event-codes).
+
+---
+
## 📝 License
Licensed under the [Apache License](./LICENSE).
diff --git a/assets/pinterest.png b/assets/pinterest.png
deleted file mode 100644
index 7ade056..0000000
Binary files a/assets/pinterest.png and /dev/null differ
diff --git a/assets/pinterest.svg b/assets/pinterest.svg
new file mode 100644
index 0000000..77c7e38
--- /dev/null
+++ b/assets/pinterest.svg
@@ -0,0 +1,15 @@
+
+
+
diff --git a/manifest.json b/manifest.json
index 5002806..8c8698c 100644
--- a/manifest.json
+++ b/manifest.json
@@ -2,7 +2,7 @@
"name": "Pinterest",
"namespace": "pinterest",
"description": "Pinterest Managed Component",
- "icon": "assets/icon.svg",
+ "icon": "assets/pinterest.svg",
"categories": ["Analytics"],
"provides": ["events"],
"allowCustomFields": true,
diff --git a/src/index.test.ts b/src/index.test.ts
index e50089e..0e535fb 100644
--- a/src/index.test.ts
+++ b/src/index.test.ts
@@ -13,34 +13,20 @@ describe('Pinterest MC sends correct request', () => {
const baseHost = `${baseHostname}:${port}`
const baseOrigin = `https://${baseHost}`
const baseHref = `${baseOrigin}/`
- const searchParams = new URLSearchParams()
- const mockEvent = new Event('pagevisit', {}) as MCEvent
- mockEvent.name = 'Pinterest Test'
- mockEvent.payload = { timestamp: 1670409810, event: 'pagevisit', tid: 'xyz' }
- mockEvent.client = {
- url: {
- href: baseHref,
- origin: baseOrigin,
- protocol: 'http:',
- username: '',
- password: '',
- host: baseHost,
- hostname: baseHostname,
- port: port,
- pathname: '/',
- search: '',
- searchParams: searchParams,
- hash: '',
+ const mockEvent: MCEvent = {
+ payload: { timestamp: 1670409810, event: 'pageview', tid: 'xyz' },
+ client: {
+ url: new URL(baseHref),
+ title: 'Zaraz "Test" /t Page',
+ timestamp: 1670409810,
+ userAgent:
+ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
+ language: 'en-GB',
+ referer: `${baseOrigin}/somewhere-else.html`,
+ ip: baseHostname,
+ emitter: 'browser',
},
- title: 'Zaraz "Test" /t Page',
- timestamp: 1670409810,
- userAgent:
- 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
- language: 'en-GB',
- referer: `${baseOrigin}/somewhere-else.html`,
- ip: baseHostname,
- emitter: 'browser',
}
const settings: ComponentSettings = {}
@@ -83,7 +69,7 @@ describe('Pinterest MC sends correct request', () => {
'pd[tm]': 'pinterest-mc',
ed: '{"timestamp":1670409810,"event":"pagevisit"}',
}
- const requestUrl = getRequestUrl(rawRequestBody, mockEvent, settings)
+ const requestUrl = getRequestUrl(rawRequestBody)
const requestUrlDecoded = decodeURI(requestUrl)
const expectedUrl = `https://ct.pinterest.com/v3/?ad={"loc"%3A"https%3A%2F%2F127.0.0.1%3A1337%2F"%2C"ref"%3A"https%3A%2F%2F127.0.0.1%3A1337%2Fsomewhere-else.html"%2C"if"%3Afalse%2C"mh"%3A"2424edb5"}&cb=1671006315874&tid=xyz&event=pagevisit&pd[tm]=pinterest-mc&ed={"timestamp"%3A1670409810%2C"event"%3A"pagevisit"}`
@@ -93,7 +79,7 @@ describe('Pinterest MC sends correct request', () => {
it('Handler invokes fetch correctly', () => {
const arr = []
- handler('pageview', mockEvent, settings, (...args) => {
+ handler(mockEvent, settings, 'pageview', (...args) => {
arr.push(args)
})
expect(arr.length).toBe(1)
diff --git a/src/index.ts b/src/index.ts
index 102e690..e56a75c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -20,6 +20,82 @@ export type RequestBodyType = {
ed?: string
}
+type EcommerceType = {
+ order_id: number | string
+ currency: string
+ revenue: number | string
+ total: number | string
+ value: number | string
+ quantity: number | string
+ products: Product[] | null
+ checkout_id: number | string
+ affiliation: string
+ shipping: number | string
+ tax: number | string
+ discount: number | string
+ coupon: string
+ creative: string
+ query: string
+ step: number | string
+ payment_type: string
+}
+
+export type Product = {
+ product_id: number | string
+ sku: number | string
+ name: string
+ category: string
+ brand: string
+ price: number | string
+ quantity: number
+ variant: string
+ currency: string
+ value: number | string
+ position: number | string
+ coupon: number | string
+}
+const eventMappings: { [key: string]: string } = {
+ 'Product Added': 'addtocart',
+ 'Order Completed': 'checkout',
+ 'Products Searched': 'search',
+}
+function mapEcommerceEvent(eventName: string): string | undefined {
+ return eventMappings[eventName] || undefined
+}
+function mapEcommerceData(
+ ecommerce: EcommerceType
+): Record | null {
+ const transformedProductData: Record = {}
+ if (!ecommerce) {
+ return null
+ } else {
+ ecommerce.products?.forEach((product, index) =>
+ [
+ 'product_id',
+ 'sku',
+ 'category',
+ 'name',
+ 'brand',
+ 'variant',
+ 'price',
+ ].forEach(prop => {
+ const key = `product_${prop}[${index}]`
+ transformedProductData[key] =
+ product[prop as keyof Product] || product.sku
+ })
+ )
+ }
+
+ const ecommerceData = {
+ order_id: ecommerce.order_id,
+ currency: ecommerce.currency,
+ value: ecommerce.revenue || ecommerce.total || ecommerce.value,
+ order_quantity: ecommerce.quantity,
+ ...transformedProductData,
+ }
+ return ecommerceData
+}
+
export const getRequestBody = (
eventType: string,
event: MCEvent,
@@ -44,8 +120,7 @@ export const getRequestBody = (
event: eventType,
}
- const { 'pd[em]': pdem, tid, ...cleanPayload } = payload
-
+ const { pdem, tid, ecommerce, ...cleanPayload } = payload
// pd - partner data
if (pdem) {
requestBody['pd[em]'] = pdem
@@ -53,11 +128,17 @@ export const getRequestBody = (
requestBody['pd[tm]'] = payload.tm || 'pinterest-mc'
+ // match event types to Pinterest's default
+
+ const ecommerceData = mapEcommerceData(ecommerce)
+ for (const key in ecommerceData) {
+ cleanPayload[key] = ecommerceData[key]
+ }
+
if (Object.keys(cleanPayload).length) {
- // event data
+ // event data is created, note that it also holds the ecommerce parameters
requestBody['ed'] = JSON.stringify(cleanPayload)
}
-
return requestBody
}
@@ -75,54 +156,41 @@ export const sendRequest = (url: string, event: MCEvent) => {
}
export const handler = (
- eventType: string,
event: MCEvent,
settings: ComponentSettings,
+ ev: string,
customSendRequest = sendRequest
) => {
+ const eventType = event.payload.ude || ev // ude is the "user defined event" field in case of eventType ='user-defined-event'
const requestBody = getRequestBody(eventType, event, settings)
const requestUrl = getRequestUrl(requestBody)
customSendRequest(requestUrl, event)
}
export default async function (manager: Manager, settings: ComponentSettings) {
- manager.addEventListener('pageview', event => {
- handler('pagevisit', event, settings)
+ const events = [
+ 'pageview',
+ 'lead',
+ 'signup',
+ 'watchvideo',
+ 'viewcategory',
+ 'custom',
+ 'addtocart',
+ 'checkout',
+ 'search',
+ 'user-defined-event',
+ ]
+ events.forEach(ev => {
+ manager.addEventListener(ev, event => {
+ handler(event, settings, ev)
+ })
})
-
- manager.addEventListener('addtocart', event => {
- handler('addtocart', event, settings)
- })
-
- manager.addEventListener('checkout', event => {
- handler('checkout', event, settings)
- })
-
- manager.addEventListener('lead', event => {
- handler('lead', event, settings)
- })
-
- manager.addEventListener('signup', event => {
- handler('signup', event, settings)
- })
-
- manager.addEventListener('viewcategory', event => {
- handler('viewcategory', event, settings)
- })
- manager.addEventListener('watchvideo', event => {
- handler('watchVideo', event, settings)
- })
-
- manager.addEventListener('custom', event => {
- handler('custom', event, settings)
- })
-
- manager.addEventListener('search', event => {
- handler('search', event, settings)
- })
-
- manager.addEventListener('userdefinedevent', event => {
- const userDefinedEvent: string = event.payload.userDefinedEvent
- handler(userDefinedEvent, event, settings)
+ manager.addEventListener('ecommerce', event => {
+ if (typeof event.name === 'string') {
+ const ev = mapEcommerceEvent(event.name)
+ if (ev) {
+ handler(event, settings, ev)
+ }
+ }
})
}