diff --git a/packages/analytics-plugin-churn-zero/.babelrc b/packages/analytics-plugin-churn-zero/.babelrc new file mode 100644 index 00000000..b5e3023e --- /dev/null +++ b/packages/analytics-plugin-churn-zero/.babelrc @@ -0,0 +1,9 @@ +{ + "presets": [ + [ + "@babel/env", { + "modules": false + } + ] + ] +} diff --git a/packages/analytics-plugin-churn-zero/.gitignore b/packages/analytics-plugin-churn-zero/.gitignore new file mode 100644 index 00000000..e9d65964 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/.gitignore @@ -0,0 +1,13 @@ +dist +node_modules +lib +yarn.lock + +*.log + +# IDE stuff +**/.idea + +# OS stuff +.DS_Store +.tmp \ No newline at end of file diff --git a/packages/analytics-plugin-churn-zero/CHANGELOG.md b/packages/analytics-plugin-churn-zero/CHANGELOG.md new file mode 100644 index 00000000..29eeacbc --- /dev/null +++ b/packages/analytics-plugin-churn-zero/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.0.1](https://github.com/DavidWells/analytics/compare/analytics-plugin-churn-zero@0.0.1) (2024-08-27) + +**Note:** Initial version for package analytics-plugin-churn-zero + +### Features + +- **plugin-churn-zero:** add churn-zero plugin ([](https://github.com/DavidWells/analytics/commit/)) diff --git a/packages/analytics-plugin-churn-zero/README.md b/packages/analytics-plugin-churn-zero/README.md new file mode 100644 index 00000000..b750fc3a --- /dev/null +++ b/packages/analytics-plugin-churn-zero/README.md @@ -0,0 +1,336 @@ + +# ChurnZero plugin for `analytics` + +Integration with ChurnZero for [analytics](https://www.npmjs.com/package/analytics) + +This analytics plugin will load ChurnZero's client side tracking script into your application and send custom events, page views, and identify visitors inside ChurnZero. + +[View the docs](https://getanalytics.io/plugins/churn-zero/) + + +
+Click to expand + +- [Installation](#installation) +- [How to use](#how-to-use) +- [Platforms Supported](#platforms-supported) +- [Browser usage](#browser-usage) + - [Browser API](#browser-api) + - [Configuration options for browser](#configuration-options-for-browser) +- [Server-side usage](#server-side-usage) + - [Server-side API](#server-side-api) + - [Configuration options for server-side](#configuration-options-for-server-side) +- [Additional examples](#additional-examples) + +
+ + +## Installation + +Install `analytics` and `@analytics/churn-zero` packages + +```bash +npm install analytics +npm install @analytics/churn-zero +``` + + + +## How to use + +The `@analytics/churn-zero` package works in [the browser](#browser-usage) and [server-side in Node.js](#server-side-usage). To use, install the package, include in your project and initialize the plugin with [analytics](https://www.npmjs.com/package/analytics). + +Below is an example of how to use the browser plugin. + +```js +import Analytics from 'analytics' +import churnZeroPlugin from '@analytics/churn-zero' + +const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + // This will load crazy egg on to the page + churnZeroPlugin({ + accountId: '1234578' + }) + ] +}) + +/* Track a page view */ +analytics.page() + +/* Track a custom event */ +analytics.track('cartCheckout', { + item: 'pink socks', + price: 20 +}) + +/* Identify a visitor */ +analytics.identify('user-id-xyz', { + firstName: 'bill', + lastName: 'murray' +}) + +``` + +After initializing `analytics` with the `churnZeroPlugin` plugin, data will be sent into ChurnZero whenever [analytics.page](https://getanalytics.io/api/#analyticspage), [analytics.track](https://getanalytics.io/api/#analyticstrack), or [analytics.identify](https://getanalytics.io/api/#analyticsidentify) are called. + +See [additional implementation examples](#additional-examples) for more details on using in your project. + +## Platforms Supported + +The `@analytics/churn-zero` package works in [the browser](#browser-usage) and [server-side in Node.js](#server-side-usage) + +## Browser usage + +The ChurnZero client side browser plugin works with these analytic api methods: + +- **[analytics.identify](https://getanalytics.io/api/#analyticsidentify)** - Identify visitors and send details to ChurnZero +- **[analytics.page](https://getanalytics.io/api/#analyticspage)** - Sends page views into ChurnZero +- **[analytics.track](https://getanalytics.io/api/#analyticstrack)** - Track custom events and send to ChurnZero + +### Browser API + +```js +import Analytics from 'analytics' +import churnZeroPlugin from '@analytics/churn-zero' + +const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + // This will load crazy egg on to the page + churnZeroPlugin({ + accountId: '1234578' + }) + ] +}) + +``` + +### Configuration options for browser + +| Option | description | +|:---------------------------|:-----------| +| `accountId`
**required** - string| ChurnZero account ID | +| `scriptInclude`
_optional_ - boolean| Load ChurnZero script into page | +| `options`
_optional_ - object| ChurnZero script options | + +## Server-side usage + +The ChurnZero server-side node.js plugin works with these analytic api methods: + +- **[analytics.page](https://getanalytics.io/api/#analyticspage)** - Sends page views into ChurnZero +- **[analytics.track](https://getanalytics.io/api/#analyticstrack)** - Track custom events and send to ChurnZero +- **[analytics.identify](https://getanalytics.io/api/#analyticsidentify)** - Identify visitors and send details to ChurnZero + +### Server-side API + +```js +import Analytics from 'analytics' +import churnZeroPlugin from '@analytics/churn-zero' + +const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + churnZeroPlugin({ + apiKey: 'abc123' + }) + ] +}) + +``` + +### Configuration options for server-side + +| Option | description | +|:---------------------------|:-----------| +| `apiKey`
**required** - string| ChurnZero API key | + + +## Additional examples + +Below are additional implementation examples. + +
+ Server-side ES6 + + ```js + import Analytics from 'analytics' + import churnZeroPlugin from '@analytics/churn-zero' + + const analytics = Analytics({ + app: 'awesome-app', + plugins: [ + churnZeroPlugin({ + apiKey: 'abc123' + }) + // ...other plugins + ] + }) + + /* Track a page view */ + analytics.page() + + /* Track a custom event */ + analytics.track('cartCheckout', { + item: 'pink socks', + price: 20 + }) + + /* Identify a visitor */ + analytics.identify('user-id-xyz', { + firstName: 'bill', + lastName: 'murray' + }) + + ``` + +
+ +
+ Server-side Node.js with common JS + + If using node, you will want to import the `.default` + + ```js + const analyticsLib = require('analytics').default + const churnZeroPlugin = require('@analytics/churn-zero').default + + const analytics = analyticsLib({ + app: 'my-app-name', + plugins: [ + churnZeroPlugin({ + apiKey: 'abc123' + }) + ] + }) + + /* Track a page view */ + analytics.page() + + /* Track a custom event */ + analytics.track('cartCheckout', { + item: 'pink socks', + price: 20 + }) + + /* Identify a visitor */ + analytics.identify('user-id-xyz', { + firstName: 'bill', + lastName: 'murray' + }) + + ``` + +
+ +
+ Using in HTML + + Below is an example of importing via the unpkg CDN. Please note this will pull in the latest version of the package. + + ```html + + + + Using @analytics/churn-zero in HTML + + + + + + .... + + + + ``` + +
+ +
+ Using in HTML via ES Modules + + Using `@analytics/churn-zero` in ESM modules. + + ```html + + + + Using @analytics/churn-zero in HTML via ESModules + + + + + .... + + + + ``` + +
+ + + diff --git a/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.cjs.js b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.cjs.js new file mode 100644 index 00000000..212bbd62 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.cjs.js @@ -0,0 +1,89 @@ +'use strict'; + +require('unfetch'); + +/* global ChurnZero */ + +/** + * ChurnZero plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://support.churnzero.com/hc/en-us/articles/360004683552-Integrate-ChurnZero-using-Javascript + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero AppKey + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * This will load ChurnZero on to the page + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin() { + var pluginConfig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + name: 'churn-zero', + config: pluginConfig, + initialize: function initialize(_ref) { + var config = _ref.config; + var appKey = config.appKey, + subdomain = config.subdomain; + if (!appKey) { + throw new Error('No ChurnZero appKey defined'); + } + if (!subdomain) { + throw new Error('No ChurnZero subdomain defined'); + } + + // Create script & append to DOM + var script = document.createElement('script'); + var firstScript = document.getElementsByTagName('script')[0]; + script.type = 'text/javascript'; + script.async = true; + script.src = "https://".concat(subdomain, ".churnzero.net/churnzero.js"); + firstScript.parentNode.insertBefore(script, firstScript); + }, + identify: function identify(_ref2) { + var payload = _ref2.payload, + config = _ref2.config; + var _payload$traits = payload.traits, + company = _payload$traits.company, + email = _payload$traits.email, + firstName = _payload$traits.firstName, + lastName = _payload$traits.lastName; + if (typeof ChurnZero === 'undefined') return; + if (config.appKey) { + ChurnZero.push(['setAppKey', config.appKey]); + } + if (company && company.id && email) { + ChurnZero.push(['setContact', company.id, email]); + } + if (firstName) { + ChurnZero.push(['setAttribute', 'contact', 'FirstName', firstName]); + } + if (lastName) { + ChurnZero.push(['setAttribute', 'contact', 'LastName', lastName]); + } + if (email) { + ChurnZero.push(['setAttribute', 'contact', 'Email', email]); + } + }, + track: function track(_ref3) { + var payload = _ref3.payload, + config = _ref3.config; + if (typeof ChurnZero === 'undefined') return; + if (config.whitelistedEvents && !config.whitelistedEvents.includes(payload.event)) return; + ChurnZero.push(['trackEvent', payload.event, undefined, undefined, payload.properties]); + }, + loaded: function loaded() { + return !!window.ChurnZero; + } + }; +} + +/* This module will shake out unused code and work in browser and node 🎉 */ +var index = churnZeroPlugin ; + +module.exports = index; diff --git a/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.es.js b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.es.js new file mode 100644 index 00000000..729c3c6a --- /dev/null +++ b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.browser.es.js @@ -0,0 +1,87 @@ +import 'unfetch'; + +/* global ChurnZero */ + +/** + * ChurnZero plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://support.churnzero.com/hc/en-us/articles/360004683552-Integrate-ChurnZero-using-Javascript + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero AppKey + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * This will load ChurnZero on to the page + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin() { + var pluginConfig = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + return { + name: 'churn-zero', + config: pluginConfig, + initialize: function initialize(_ref) { + var config = _ref.config; + var appKey = config.appKey, + subdomain = config.subdomain; + if (!appKey) { + throw new Error('No ChurnZero appKey defined'); + } + if (!subdomain) { + throw new Error('No ChurnZero subdomain defined'); + } + + // Create script & append to DOM + var script = document.createElement('script'); + var firstScript = document.getElementsByTagName('script')[0]; + script.type = 'text/javascript'; + script.async = true; + script.src = "https://".concat(subdomain, ".churnzero.net/churnzero.js"); + firstScript.parentNode.insertBefore(script, firstScript); + }, + identify: function identify(_ref2) { + var payload = _ref2.payload, + config = _ref2.config; + var _payload$traits = payload.traits, + company = _payload$traits.company, + email = _payload$traits.email, + firstName = _payload$traits.firstName, + lastName = _payload$traits.lastName; + if (typeof ChurnZero === 'undefined') return; + if (config.appKey) { + ChurnZero.push(['setAppKey', config.appKey]); + } + if (company && company.id && email) { + ChurnZero.push(['setContact', company.id, email]); + } + if (firstName) { + ChurnZero.push(['setAttribute', 'contact', 'FirstName', firstName]); + } + if (lastName) { + ChurnZero.push(['setAttribute', 'contact', 'LastName', lastName]); + } + if (email) { + ChurnZero.push(['setAttribute', 'contact', 'Email', email]); + } + }, + track: function track(_ref3) { + var payload = _ref3.payload, + config = _ref3.config; + if (typeof ChurnZero === 'undefined') return; + if (config.whitelistedEvents && !config.whitelistedEvents.includes(payload.event)) return; + ChurnZero.push(['trackEvent', payload.event, undefined, undefined, payload.properties]); + }, + loaded: function loaded() { + return !!window.ChurnZero; + } + }; +} + +/* This module will shake out unused code and work in browser and node 🎉 */ +var index = churnZeroPlugin ; + +export { index as default }; diff --git a/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.cjs.js b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.cjs.js new file mode 100644 index 00000000..1fc57383 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.cjs.js @@ -0,0 +1,112 @@ +'use strict'; + +var fetch = require('unfetch'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var fetch__default = /*#__PURE__*/_interopDefaultLegacy(fetch); + +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } + +/** + * Custify server plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://docs.churn-zero.com/ + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero API key + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin(_ref) { + var appKey = _ref.appKey, + subdomain = _ref.subdomain, + accountExternalId = _ref.accountExternalId, + contactExternalId = _ref.contactExternalId; + var _loaded = false; + return { + name: 'churn-zero', + config: { + appKey: appKey, + subdomain: subdomain, + accountExternalId: accountExternalId, + contactExternalId: contactExternalId + }, + track: function track(_ref2) { + var payload = _ref2.payload; + var _payload$traits = payload.traits, + company = _payload$traits.company, + email = _payload$traits.email, + event = _payload$traits.event, + properties = _payload$traits.properties; + makeChurnZeroRequest(config, { + action: 'trackEvent', + eventName: event, + cf_metadata: properties, + accountExternalId: company ? company.id : undefined, + contactExternalId: email + }); + }, + identify: function identify(_ref3) { + var payload = _ref3.payload; + var _payload$traits2 = payload.traits, + company = _payload$traits2.company, + email = _payload$traits2.email, + name = _payload$traits2.name, + firstName = _payload$traits2.firstName, + lastName = _payload$traits2.lastName; + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'account', + attr_Name: name, + accountExternalId: company.id, + contactExternalId: email + }); + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'contact', + attr_FirstName: firstName, + attr_LastName: lastName, + attr_Email: email, + accountExternalId: company.id, + contactExternalId: email + }); + }, + loaded: function loaded() { + return _loaded; + } + }; +} +function makeChurnZeroRequest(_ref4) { + var config = _ref4.config, + body = _ref4.body; + return fetch__default["default"]("https://".concat(config.subdomain, ".churnzero.net/i"), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache' + }, + body: JSON.stringify(_objectSpread(_objectSpread({ + appKey: config.appKey + }, body), {}, { + accountExternalId: body.accountExternalId || config.accountExternalId, + contactExternalId: body.contactExternalId || config.contactExternalId + })) + }); +} + +/* This module will shake out unused code and work in browser and node 🎉 */ +var index = churnZeroPlugin; + +module.exports = index; diff --git a/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.es.js b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.es.js new file mode 100644 index 00000000..908f9e64 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/lib/analytics-plugin-churn-zero.es.js @@ -0,0 +1,106 @@ +import fetch from 'unfetch'; + +function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } +function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } +function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } +function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } + +/** + * Custify server plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://docs.churn-zero.com/ + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero API key + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin(_ref) { + var appKey = _ref.appKey, + subdomain = _ref.subdomain, + accountExternalId = _ref.accountExternalId, + contactExternalId = _ref.contactExternalId; + var _loaded = false; + return { + name: 'churn-zero', + config: { + appKey: appKey, + subdomain: subdomain, + accountExternalId: accountExternalId, + contactExternalId: contactExternalId + }, + track: function track(_ref2) { + var payload = _ref2.payload; + var _payload$traits = payload.traits, + company = _payload$traits.company, + email = _payload$traits.email, + event = _payload$traits.event, + properties = _payload$traits.properties; + makeChurnZeroRequest(config, { + action: 'trackEvent', + eventName: event, + cf_metadata: properties, + accountExternalId: company ? company.id : undefined, + contactExternalId: email + }); + }, + identify: function identify(_ref3) { + var payload = _ref3.payload; + var _payload$traits2 = payload.traits, + company = _payload$traits2.company, + email = _payload$traits2.email, + name = _payload$traits2.name, + firstName = _payload$traits2.firstName, + lastName = _payload$traits2.lastName; + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'account', + attr_Name: name, + accountExternalId: company.id, + contactExternalId: email + }); + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'contact', + attr_FirstName: firstName, + attr_LastName: lastName, + attr_Email: email, + accountExternalId: company.id, + contactExternalId: email + }); + }, + loaded: function loaded() { + return _loaded; + } + }; +} +function makeChurnZeroRequest(_ref4) { + var config = _ref4.config, + body = _ref4.body; + return fetch("https://".concat(config.subdomain, ".churnzero.net/i"), { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache' + }, + body: JSON.stringify(_objectSpread(_objectSpread({ + appKey: config.appKey + }, body), {}, { + accountExternalId: body.accountExternalId || config.accountExternalId, + contactExternalId: body.contactExternalId || config.contactExternalId + })) + }); +} + +/* This module will shake out unused code and work in browser and node 🎉 */ +var index = churnZeroPlugin; + +export { index as default }; diff --git a/packages/analytics-plugin-churn-zero/package.json b/packages/analytics-plugin-churn-zero/package.json new file mode 100644 index 00000000..44042fe0 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/package.json @@ -0,0 +1,57 @@ +{ + "name": "@analytics/churn-zero", + "version": "0.0.1", + "description": "Churnzero integration for 'analytics' module for browser & node", + "projectMeta": { + "provider": { + "name": "ChurnZero", + "url": "https://www.churnzero.com/" + }, + "platforms": { + "browser": "./src/browser.js", + "node": "./src/node.js" + } + }, + "keywords": [ + "analytics", + "analytics-project", + "analytics-plugin", + "churn-zero" + ], + "author": "Ahmed Onawale ", + "license": "MIT", + "scripts": { + "docs": "node ../analytics-cli/bin/run docs", + "build": "node ../../scripts/build/index.js", + "watch": "node ../../scripts/build/_watch.js", + "release:patch": "npm version patch && npm publish", + "release:minor": "npm version minor && npm publish", + "release:major": "npm version major && npm publish", + "es": "../../node_modules/.bin/babel-node ./testBabel.js" + }, + "main": "lib/analytics-plugin-churn-zero.cjs.js", + "globalName": "analyticsChurnzero", + "jsnext:main": "lib/analytics-plugin-churn-zero.es.js", + "module": "lib/analytics-plugin-churn-zero.es.js", + "browser": { + "./lib/analytics-plugin-churn-zero.cjs.js": "./lib/analytics-plugin-churn-zero.browser.cjs.js", + "./lib/analytics-plugin-churn-zero.es.js": "./lib/analytics-plugin-churn-zero.browser.es.js" + }, + "files": [ + "dist", + "lib", + "README.md" + ], + "homepage": "https://github.com/DavidWells/analytics#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/DavidWells/analytics.git" + }, + "devDependencies": { + "@babel/core": "^7.2.2", + "@babel/preset-env": "^7.3.1" + }, + "dependencies": { + "unfetch": "^4.2.0" + } +} diff --git a/packages/analytics-plugin-churn-zero/src/browser.js b/packages/analytics-plugin-churn-zero/src/browser.js new file mode 100644 index 00000000..81d34c3b --- /dev/null +++ b/packages/analytics-plugin-churn-zero/src/browser.js @@ -0,0 +1,77 @@ +/* global ChurnZero */ + +/** + * ChurnZero plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://support.churnzero.com/hc/en-us/articles/360004683552-Integrate-ChurnZero-using-Javascript + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero AppKey + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * This will load ChurnZero on to the page + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin(pluginConfig = {}) { + return { + name: 'churn-zero', + config: pluginConfig, + initialize: ({ config }) => { + const { appKey, subdomain } = config + if (!appKey) { + throw new Error('No ChurnZero appKey defined') + } + if (!subdomain) { + throw new Error('No ChurnZero subdomain defined') + } + + // Create script & append to DOM + const script = document.createElement('script'); + const firstScript = document.getElementsByTagName('script')[0]; + script.type = 'text/javascript'; + script.async = true; + script.src = `https://${subdomain}.churnzero.net/churnzero.js`; + firstScript.parentNode.insertBefore(script, firstScript); + }, + identify: ({ payload, config }) => { + const { company, email, firstName, lastName } = payload.traits + + if (typeof ChurnZero === 'undefined') return + + if (config.appKey) { + ChurnZero.push(['setAppKey', config.appKey]); + } + + if (company && company.id && email) { + ChurnZero.push(['setContact', company.id, email]); + } + + if (firstName) { + ChurnZero.push(['setAttribute', 'contact', 'FirstName', firstName]); + } + if (lastName) { + ChurnZero.push(['setAttribute', 'contact', 'LastName', lastName]); + } + if (email) { + ChurnZero.push(['setAttribute', 'contact', 'Email', email]); + } + }, + track: ({ payload, config }) => { + if (typeof ChurnZero === 'undefined') return + + if (config.whitelistedEvents && !config.whitelistedEvents.includes(payload.event)) return + + ChurnZero.push(['trackEvent', payload.event, undefined, undefined, payload.properties]); + }, + loaded: () => { + return !!window.ChurnZero + }, + } +} + +export default churnZeroPlugin diff --git a/packages/analytics-plugin-churn-zero/src/index.js b/packages/analytics-plugin-churn-zero/src/index.js new file mode 100644 index 00000000..77aac893 --- /dev/null +++ b/packages/analytics-plugin-churn-zero/src/index.js @@ -0,0 +1,5 @@ +import nodeCode from './node' +import browser from './browser' + +/* This module will shake out unused code and work in browser and node 🎉 */ +export default process.browser ? browser : nodeCode diff --git a/packages/analytics-plugin-churn-zero/src/node.js b/packages/analytics-plugin-churn-zero/src/node.js new file mode 100644 index 00000000..cba9591f --- /dev/null +++ b/packages/analytics-plugin-churn-zero/src/node.js @@ -0,0 +1,81 @@ +import fetch from 'unfetch' + +/** + * Custify server plugin + * @link https://getanalytics.io/plugins/churn-zero + * @link https://docs.churn-zero.com/ + * @param {object} pluginConfig - Plugin settings + * @param {string} pluginConfig.appKey - ChurnZero API key + * @param {string} pluginConfig.subdomain - ChurnZero AppKey + * @param {string} pluginConfig.whitelistedEvents - An optional list of events to track + * @return {AnalyticsPlugin} + * @example + * + * churnZeroPlugin({ + * appKey: '1234578' + * subdomain: 'mycompanydomain' + * }) + */ +function churnZeroPlugin({ appKey, subdomain, accountExternalId, contactExternalId }) { + let loaded = false; + return { + name: 'churn-zero', + config: { + appKey, + subdomain, + accountExternalId, + contactExternalId, + }, + track: ({ payload }) => { + const { company, email, event, properties } = payload.traits + + makeChurnZeroRequest(config, { + action: 'trackEvent', + eventName: event, + cf_metadata: properties, + accountExternalId: company ? company.id : undefined, + contactExternalId: email, + }); + }, + identify: ({ payload }) => { + const { company, email, name, firstName, lastName } = payload.traits + + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'account', + attr_Name: name, + accountExternalId: company.id, + contactExternalId: email, + }); + + makeChurnZeroRequest(config, { + action: 'setAttribute', + entity: 'contact', + attr_FirstName: firstName, + attr_LastName: lastName, + attr_Email: email, + accountExternalId: company.id, + contactExternalId: email, + }); + }, + loaded: () => loaded, + }; +} + +function makeChurnZeroRequest({config, body}) { + return fetch(`https://${config.subdomain}.churnzero.net/i`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'no-cache', + }, + body: JSON.stringify({ + appKey: config.appKey, + ...body, + accountExternalId: body.accountExternalId || config.accountExternalId, + contactExternalId: body.contactExternalId || config.contactExternalId, + }), + }); +} + +export default churnZeroPlugin