diff --git a/AnonymousUserEventTracking.md b/AnonymousUserEventTracking.md
new file mode 100644
index 00000000..72a99b2f
--- /dev/null
+++ b/AnonymousUserEventTracking.md
@@ -0,0 +1,138 @@
+# Anonymous User Event Tracking Iterable's Web SDK
+The Anonymous User Tracking Module is a pivotal component within our WEB SDK that seamlessly captures user events while maintaining anonymity for non-logged-in users. This module is designed to diligently gather and store events triggered by users who have not yet signed in. Once specific criteria are met or when the user decides to engage further, the module securely synchronizes all accumulated anonymous events with our servers.
+
+By adopting a privacy-first approach, the module allows us to gain valuable insights into user interactions without compromising their personal information. As users transition from anonymous to logged-in status, the module smoothly transitions as well, associating future events with the user's unique identity while ensuring the continuity of event tracking.
+
+Key Features:
+
+- Anonymous Event Tracking: Captures a diverse range of user events even before they log in, ensuring a comprehensive understanding of user behavior.
+Privacy Protection: Safeguards user anonymity by collecting and storing events without requiring personal information.
+- Event Synchronization: Upon meeting predefined conditions, securely transmits all anonymous events to the server, enabling data-driven decision-making.
+- Seamless User Transition: Effortlessly shifts from tracking anonymous events to associating events with specific users as they log in.
+- Enhanced Insights: Provides a holistic view of user engagement patterns, contributing to a more informed product optimization strategy.
+Implementing the Anonymous
+
+# Installation
+
+To install this SDK through NPM:
+
+```
+$ npm install @iterable/web-sdk
+```
+
+with yarn:
+
+```
+$ yarn add @iterable/web-sdk
+```
+
+or with a CDN:
+
+```js
+
+```
+
+# Methods
+- [`getAnonCriteria`]
+- [`trackAnonEvent`]
+- [`trackAnonPurchaseEvent`]
+- [`trackAnonUpdateCart`]
+- [`createUser`]
+- [`syncEvents`]
+- [`checkCriteriaCompletion`]
+
+# Usage
+
+1. `trackAnonEvent`
+The 'trackAnonEvent' function within the Iterable-Web SDK empowers seamless tracking of diverse web events. Developers can enrich event data with specific metadata using the 'dataFields' attribute. This function intelligently distinguishes between logged-in and non-logged-in users, securely storing event data on the server post-login, while locally preserving data for anonymous users, ensuring comprehensive event monitoring in both scenarios.
+
+```ts
+const eventDetails = {
+ ...conditionalParams,
+ createNewFields: true,
+ createdAt: (Date.now() / 1000) | 0,
+ dataFields: { website: { domain: 'omni.com' }, eventType: 'track' },
+};
+
+await anonymousUserEventManager.trackAnonEvent(eventDetails);
+```
+
+2. `trackAnonPurchaseEvent`
+The 'trackAnonPurchaseEvent' function in the Iterable-Web SDK enables precise tracking of purchase-related web events. Developers can seamlessly include specific details about purchased items. With an innate understanding of user authentication status, the function securely stores event data on the server post-login, while also providing localized storage for non-logged-in users, guaranteeing comprehensive event monitoring in both usage scenarios.
+
+```ts
+const eventDetails = {
+ ...conditionalParams,
+ items: [{ name: purchaseItem, id: 'fdsafds', price: 100, quantity: 2 }],
+ total: 200
+}
+
+await anonymousUserEventManager.trackAnonPurchaseEvent(eventDetails);
+```
+
+3. `trackAnonUpdateCart`
+The 'trackAnonUpdateCart' function in the Iterable-Web SDK empowers effortless tracking of web events related to cart updates. Developers can accurately outline details for multiple items within the cart. It seamlessly handles data, securely transmitting events to the server upon user login, while also providing local storage for event details in the absence of user login, ensuring comprehensive event tracking in all scenarios.
+
+```ts
+const eventDetails = {
+ ...conditionalParams,
+ items: [{ name: cartItem, id: 'fdsafds', price: 100, quantity: 2 }]
+}
+
+await anonymousUserEventManager.trackAnonUpdateCart(eventDetails);
+```
+
+4. `createUser`
+The 'createUser' function in the Iterable-Web SDK facilitates user creation by assigning a unique user UUID. This function also supports the seamless updating of user details on the server, providing a comprehensive solution for managing user data within your application.
+
+```ts
+await anonymousUserEventManager.createUser(uuid, process.env.API_KEY);
+```
+
+5. `getAnonCriteria`
+The 'getAnonCriteria' function within the Iterable-Web SDK retrieves criteria from the server for matching purposes. It efficiently fetches and returns an array of criteria, providing developers with essential tools to enhance their application's functionality through data-driven decision-making.
+
+```ts
+const criteriaList = await anonymousUserEventManager.getAnonCriteria();
+```
+
+6. `checkCriteriaCompletion`
+The 'checkCriteriaCompletion' function in the Iterable-Web SDK performs a local assessment of stored events to determine if they fulfill specific criteria. If any of the stored events satisfy the criteria, the function returns 'true', offering developers a reliable method to validate the completion status of predefined conditions based on accumulated event data.
+
+```ts
+const isCriteriaCompleted = await anonymousUserEventManager.checkCriteriaCompletion();
+```
+
+7. `syncEvents`
+The 'syncEvents' function within the Iterable-Web SDK facilitates the seamless synchronization of locally stored events to the server while sequentially maintaining their order. This function efficiently transfers all accumulated events, clearing the local storage in the process, ensuring data consistency and integrity between the client and server-side environments.
+
+```ts
+await anonymousUserEventManager.syncEvents();
+```
+
+# Example
+
+```ts
+const eventDetails = {
+ ...conditionalParams,
+ createNewFields: true,
+ createdAt: (Date.now() / 1000) | 0,
+ userId: loggedInUser,
+ dataFields: { website: { domain: 'omni.com' }, eventType: 'track' },
+ deviceInfo: {
+ appPackageName: 'my-website'
+ }
+};
+
+await anonymousUserEventManager.trackAnonEvent(eventDetails);
+const isCriteriaCompleted = await anonymousUserEventManager.checkCriteriaCompletion();
+
+if (isCriteriaCompleted) {
+ const userId = uuidv4();
+ const App = await initialize(process.env.API_KEY);
+ await App.setUserID(userId);
+ await anonymousUserEventManager.createUser(userId, process.env.API_KEY);
+ setLoggedInUser({ type: 'user_update', data: userId });
+ await anonymousUserEventManager.syncEvents();
+}
+```
diff --git a/README.md b/README.md
index 7f39c290..74f86699 100644
--- a/README.md
+++ b/README.md
@@ -2343,20 +2343,17 @@ At that point, further requests to Iterable's API will fail.
To perform a manual JWT token refresh, call [`refreshJwtToken`](#refreshjwttoken).
-# Iterable's European data center (EUDC)
+# Iterable's European data center (EDC)
-If your Iterable project is hosted on Iterable's [European data center (EUDC)](https://support.iterable.com/hc/articles/17572750887444),
+If your Iterable project is hosted on Iterable's [European data center (EDC)](https://support.iterable.com/hc/articles/17572750887444),
you'll need to configure Iterable's Web SDK to interact with Iterable's EU-based
API endpoints.
-To do this, you have two options:
+To do this:
-- On the web server that hosts your site, set the `IS_EU_ITERABLE_SERVICE`
- environment variable to `true`.
-
-- Or, when use [`initializeWithConfig`](#initializeWithConfig) to initialize
- the SDK (rather then [`initialize`](#initialize)), and set set the
- `isEuIterableService` configuration option to `true`. For example:
+- Use [`initializeWithConfig`](#initializeWithConfig) to initialize the SDK
+ (rather then [`initialize`](#initialize)).
+- Set the `isEuIterableService` configuration option to `true`. For example:
```ts
import { initializeWithConfig } from '@iterable/web-sdk';
diff --git a/example/yarn.lock b/example/yarn.lock
index 464f77f3..5a9b6554 100644
--- a/example/yarn.lock
+++ b/example/yarn.lock
@@ -1470,12 +1470,19 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
-braces@^3.0.2, braces@~3.0.2:
+braces@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+ integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ dependencies:
+ fill-range "^7.1.1"
+
+braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
- fill-range "^7.0.1"
+ fill-range "^7.1.1"
browser-process-hrtime@^1.0.0:
version "1.0.0"
@@ -2379,10 +2386,17 @@ file-entry-cache@^6.0.1:
dependencies:
flat-cache "^3.0.4"
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+fill-range@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+ integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@@ -3592,11 +3606,11 @@ methods@~1.1.2:
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
- integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+ integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
dependencies:
- braces "^3.0.2"
+ braces "^3.0.3"
picomatch "^2.3.1"
mime-db@1.51.0:
diff --git a/package.json b/package.json
index 28ea079e..9910f008 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@iterable/web-sdk",
"description": "Iterable SDK for JavaScript and Node.",
- "version": "1.1.1",
+ "version": "1.1.2",
"homepage": "https://iterable.com/",
"repository": {
"type": "git",
@@ -73,7 +73,7 @@
"@types/jest": "^27.0.2",
"@types/node": "^12.7.1",
"@types/throttle-debounce": "^2.1.0",
- "@types/uuid": "^9.0.2",
+ "@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "^5.38.1",
"@typescript-eslint/parser": "^5.38.1",
"@webpack-cli/serve": "^1.6.0",
diff --git a/react-example/.eslintrc b/react-example/.eslintrc
index 1583a522..1f749a6a 100644
--- a/react-example/.eslintrc
+++ b/react-example/.eslintrc
@@ -1,13 +1,8 @@
{
- "extends": [
- "../.eslintrc",
- "plugin:react/recommended"
- ],
+ "extends": ["../.eslintrc", "plugin:react/recommended"],
"rules": {
"@typescript-eslint/no-empty-interface": "off",
- "react/react-in-jsx-scope": "off",
+ "react/react-in-jsx-scope": "off"
},
- "ignorePatterns": [
- "node_modules/"
- ]
-}
\ No newline at end of file
+ "ignorePatterns": ["node_modules/"]
+}
diff --git a/react-example/src/components/EventsForm.tsx b/react-example/src/components/EventsForm.tsx
index 7bf6650f..4a113612 100644
--- a/react-example/src/components/EventsForm.tsx
+++ b/react-example/src/components/EventsForm.tsx
@@ -28,10 +28,9 @@ export const EventsForm: FC = ({
);
const [trackEvent, setTrackEvent] = useState('');
-
const [isTrackingEvent, setTrackingEvent] = useState(false);
- const handleTrack = (e: FormEvent) => {
+ const handleTrack = async (e: FormEvent) => {
e.preventDefault();
setTrackingEvent(true);
@@ -50,7 +49,6 @@ export const EventsForm: FC = ({
})
.catch((e: any) => {
setTrackResponse(JSON.stringify(e.response.data));
- setTrackingEvent(false);
});
};
diff --git a/react-example/src/components/LoginFormWithoutJWT.tsx b/react-example/src/components/LoginFormWithoutJWT.tsx
new file mode 100644
index 00000000..5fd49ca7
--- /dev/null
+++ b/react-example/src/components/LoginFormWithoutJWT.tsx
@@ -0,0 +1,150 @@
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { ChangeEvent, FC, FormEvent, useState } from 'react';
+import styled from 'styled-components';
+
+import { TextField as _TextField } from './TextField';
+import { Button as _Button } from './Button';
+
+import { useUser } from '../context/Users';
+
+const TextField = styled(_TextField)``;
+
+const Button = styled(_Button)`
+ margin-left: 0.4em;
+ max-width: 425px;
+`;
+
+const Form = styled.form`
+ display: flex;
+ flex-flow: row;
+ align-items: center;
+ justify-content: flex-end;
+ height: 100%;
+
+ ${TextField} {
+ align-self: stretch;
+ margin-top: 5px;
+ }
+`;
+
+const StyledDiv = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+
+const Error = styled.div`
+ color: red;
+`;
+
+interface Props {
+ setEmail: (email: string) => Promise;
+ setUserId: (userId: string, merge?: boolean) => Promise;
+ logout: () => void;
+}
+
+export const LoginFormWithoutJWT: FC = ({
+ setEmail,
+ setUserId,
+ logout
+}) => {
+ const [useEmail, setUseEmail] = useState(true);
+ const [user, updateUser] = useState(process.env.LOGIN_EMAIL || '');
+
+ const [error, setError] = useState('');
+
+ const [isEditingUser, setEditingUser] = useState(false);
+
+ const { loggedInUser, setLoggedInUser } = useUser();
+
+ const handleSubmit = (e: FormEvent) => {
+ e.preventDefault();
+
+ const setUser = useEmail ? setEmail : setUserId;
+
+ setUser(user, true)
+ .then(() => {
+ setEditingUser(false);
+ setLoggedInUser({ type: 'user_update', data: user });
+ })
+ .catch(() => setError('Something went wrong!'));
+ };
+
+ const handleLogout = () => {
+ logout();
+ setLoggedInUser({ type: 'user_update', data: '' });
+ };
+
+ const handleEditUser = () => {
+ updateUser(loggedInUser);
+ setEditingUser(true);
+ };
+
+ const handleCancelEditUser = () => {
+ updateUser('');
+ setEditingUser(false);
+ };
+
+ const handleRadioChange = (e: ChangeEvent) => {
+ setUseEmail(e.target.value === 'email');
+ };
+
+ const first5 = loggedInUser.substring(0, 5);
+ const last9 = loggedInUser.substring(loggedInUser.length - 9);
+
+ return (
+ <>
+ {loggedInUser && !isEditingUser ? (
+ <>
+
+
+ >
+ ) : (
+
+
+
+ {error && {error}}
+
+ )}
+ >
+ );
+};
+
+export default LoginFormWithoutJWT;
diff --git a/react-example/src/index.tsx b/react-example/src/index.tsx
index 36d698eb..4767eff4 100644
--- a/react-example/src/index.tsx
+++ b/react-example/src/index.tsx
@@ -14,6 +14,7 @@ import { EmbeddedMessage } from './views/Embedded';
import { Link } from './components/Link';
import { LoginForm } from './components/LoginForm';
import { EmbeddedMsgs } from './views/EmbeddedMsgs';
+import AUTTesting from './views/AUTTesting';
import { UserProvider } from './context/Users';
import { EmbeddedMsgsImpressionTracker } from './views/EmbeddedMsgsImpressionTracker';
@@ -45,7 +46,8 @@ const HomeLink = styled(Link)`
authToken: process.env.API_KEY || '',
configOptions: {
isEuIterableService: false,
- dangerouslyAllowJsPopups: true
+ dangerouslyAllowJsPopups: true,
+ enableAnonTracking: true
},
generateJWT: ({ email, userID }) =>
axios
@@ -65,8 +67,16 @@ const HomeLink = styled(Link)`
)
.then((response: any) => response.data?.token)
};
- const { setEmail, setUserID, logout, refreshJwtToken } =
- initializeWithConfig(initializeParams);
+ const {
+ setEmail,
+ setUserID,
+ logout,
+ refreshJwtToken,
+ toggleAnonUserTrackingConsent
+ } = initializeWithConfig(initializeParams);
+
+ const handleConsent = (consent?: boolean) =>
+ toggleAnonUserTrackingConsent(consent);
const container = document.getElementById('root');
const root = createRoot(container);
@@ -98,6 +108,10 @@ const HomeLink = styled(Link)`
path="/embedded-msgs-impression-tracker"
element={}
/>
+ }
+ />
diff --git a/react-example/src/indexWithoutJWT.tsx b/react-example/src/indexWithoutJWT.tsx
new file mode 100644
index 00000000..bdedcbc7
--- /dev/null
+++ b/react-example/src/indexWithoutJWT.tsx
@@ -0,0 +1,101 @@
+import { initializeWithConfig, WithoutJWTParams } from '@iterable/web-sdk';
+import ReactDOM from 'react-dom';
+import './styles/index.css';
+
+import { BrowserRouter, Routes, Route } from 'react-router-dom';
+import styled from 'styled-components';
+import { Home } from './views/Home';
+import { Commerce } from './views/Commerce';
+import { Events } from './views/Events';
+import { Users } from './views/Users';
+import { InApp } from './views/InApp';
+import LoginFormWithoutJWT from './components/LoginFormWithoutJWT';
+import AUTTesting from './views/AUTTesting';
+import { EmbeddedMsgs } from './views/EmbeddedMsgs';
+import { EmbeddedMessage } from './views/Embedded';
+import { EmbeddedMsgsImpressionTracker } from './views/EmbeddedMsgsImpressionTracker';
+import { Link } from './components/Link';
+import { UserProvider } from './context/Users';
+
+const Wrapper = styled.div`
+ display: flex;
+ flex-flow: column;
+`;
+
+const RouteWrapper = styled.div`
+ width: 90%;
+ margin: 0 auto;
+`;
+
+const HeaderWrapper = styled.div`
+ display: flex;
+ flex-flow: row;
+ align-items: center;
+ justify-content: space-between;
+ margin: 1em;
+`;
+
+const HomeLink = styled(Link)`
+ width: 100px;
+`;
+
+((): void => {
+ // Here we are testing it using NON-JWT based project.
+ const initializeParams: WithoutJWTParams = {
+ authToken: process.env.API_KEY || '',
+ configOptions: {
+ isEuIterableService: false,
+ dangerouslyAllowJsPopups: true,
+ enableAnonTracking: true,
+ onAnonUserCreated: (userId: string) => {
+ console.log('onAnonUserCreated', userId);
+ }
+ }
+ };
+
+ const { setUserID, logout, setEmail, toggleAnonUserTrackingConsent } =
+ initializeWithConfig(initializeParams);
+
+ const handleConsent = (consent?: boolean) =>
+ toggleAnonUserTrackingConsent(consent);
+
+ // eslint-disable-next-line react/no-deprecated
+ ReactDOM.render(
+
+
+
+
+
+ Home
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ }
+ />
+
+
+
+
+ ,
+ document.getElementById('root')
+ );
+})();
diff --git a/react-example/src/styles/index.css b/react-example/src/styles/index.css
index 28642755..e6033d05 100644
--- a/react-example/src/styles/index.css
+++ b/react-example/src/styles/index.css
@@ -1,4 +1,5 @@
-html, body {
+html,
+body {
margin: 0;
padding: 0;
}
@@ -30,7 +31,7 @@ html, body {
}
#change-email-form input {
- margin-top: .5em;
+ margin-top: 0.5em;
flex-grow: 1;
padding: 1em;
}
@@ -42,17 +43,17 @@ html, body {
flex-flow: column;
justify-content: center;
}
-
+
.input-wrapper {
margin-right: 0;
transform: translateY(0);
}
-
+
#change-email-form button {
width: 100%;
margin-top: 1em;
}
-
+
#change-email-form input {
height: 50px;
}
@@ -62,4 +63,34 @@ footer {
display: flex;
justify-content: flex-end;
align-items: flex-end;
-}
\ No newline at end of file
+}
+
+#cookie-consent-container {
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+
+ position: fixed;
+ bottom: 0;
+ right: 0;
+
+ box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.2);
+ padding: 1em;
+ background: #fff;
+ margin: 1em;
+ max-width: 400px;
+
+ h3 {
+ margin-top: 0;
+ margin-bottom: 0.5em;
+ }
+
+ p {
+ margin-top: 0;
+ }
+
+ div {
+ display: flex;
+ gap: 0.5em;
+ }
+}
diff --git a/react-example/src/views/AUTTesting.tsx b/react-example/src/views/AUTTesting.tsx
new file mode 100644
index 00000000..e0a0ab2c
--- /dev/null
+++ b/react-example/src/views/AUTTesting.tsx
@@ -0,0 +1,304 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unused-vars */
+import { FC, FormEvent, useState } from 'react';
+import {
+ updateCart,
+ trackPurchase,
+ UpdateCartRequestParams,
+ TrackPurchaseRequestParams,
+ updateUser,
+ UpdateUserParams,
+ track,
+ InAppTrackRequestParams
+} from '@iterable/web-sdk';
+import { TextField } from '../components/TextField';
+import {
+ Button,
+ EndpointWrapper,
+ Form,
+ Heading,
+ Response
+} from './Components.styled';
+
+interface Props {
+ setConsent?: (accept: boolean) => void;
+}
+
+export const AUTTesting: FC = ({ setConsent }) => {
+ const [updateCartResponse, setUpdateCartResponse] = useState(
+ 'Endpoint JSON goes here'
+ );
+ const [trackPurchaseResponse, setTrackPurchaseResponse] = useState(
+ 'Endpoint JSON goes here'
+ );
+
+ const [cartItem, setCartItem] = useState(
+ '{"items":[{"name":"piano","id":"fdsafds","price":100,"quantity":2}, {"name":"piano2","id":"fdsafds2","price":100,"quantity":5}]}'
+ );
+
+ const [purchaseItem, setPurchaseItem] = useState(
+ '{"items":[{"name":"Black Coffee","id":"fdsafds","price":100,"quantity":2}], "total": 100}'
+ );
+
+ const [isUpdatingCart, setUpdatingCart] = useState(false);
+ const [isTrackingPurchase, setTrackingPurchase] = useState(false);
+ const [userDataField, setUserDataField] = useState(
+ ' { "dataFields": {"email": "user@example.com","furniture": [{"furnitureType": "Sofa","furnitureColor": "White","lengthInches": 40,"widthInches": 60},{"furnitureType": "Sofa","furnitureColor": "Gray","lengthInches": 20,"widthInches": 30}] }}'
+ );
+ const [isUpdatingUser, setUpdatingUser] = useState(false);
+ const [updateUserResponse, setUpdateUserResponse] = useState(
+ 'Endpoint JSON goes here'
+ );
+
+ const [trackResponse, setTrackResponse] = useState(
+ 'Endpoint JSON goes here'
+ );
+
+ const eventInput =
+ '{"eventName":"button-clicked", "dataFields": {"browserVisit.website.domain":"https://mybrand.com/socks"}}';
+ const [trackEvent, setTrackEvent] = useState(eventInput);
+ const [isTrackingEvent, setTrackingEvent] = useState(false);
+
+ const handleParseJson = (isUpdateCartCalled: boolean) => {
+ try {
+ // Parse JSON and assert its type
+ if (isUpdateCartCalled) {
+ const parsedObject = JSON.parse(cartItem) as UpdateCartRequestParams;
+ return parsedObject;
+ }
+ const parsedObject = JSON.parse(
+ purchaseItem
+ ) as TrackPurchaseRequestParams;
+ return parsedObject;
+ } catch (error) {
+ if (isUpdateCartCalled) {
+ setUpdateCartResponse(JSON.stringify(error.message));
+ } else setTrackPurchaseResponse(JSON.stringify(error.message));
+ return error;
+ }
+ };
+
+ const handleParseUserJson = () => {
+ try {
+ // Parse JSON and assert its type
+ return JSON.parse(userDataField) as UpdateUserParams;
+ } catch (error) {
+ setUpdateUserResponse(JSON.stringify(error.message));
+ return error;
+ }
+ };
+
+ const handleUpdateCart = (e: FormEvent) => {
+ e.preventDefault();
+ const jsonObj: UpdateCartRequestParams = handleParseJson(true);
+ if (jsonObj) {
+ setUpdatingCart(true);
+ try {
+ updateCart(jsonObj)
+ .then((response: any) => {
+ setUpdateCartResponse(JSON.stringify(response.data));
+ setUpdatingCart(false);
+ })
+ .catch((e: any) => {
+ setUpdateCartResponse(JSON.stringify(e));
+ setUpdatingCart(false);
+ });
+ } catch (error) {
+ setUpdateCartResponse(JSON.stringify(error.message));
+ setUpdatingCart(false);
+ }
+ }
+ };
+
+ const handleTrackPurchase = (e: FormEvent) => {
+ e.preventDefault();
+ const jsonObj: TrackPurchaseRequestParams = handleParseJson(false);
+ if (jsonObj) {
+ setTrackingPurchase(true);
+ try {
+ trackPurchase(jsonObj)
+ .then((response: any) => {
+ setTrackingPurchase(false);
+ setTrackPurchaseResponse(JSON.stringify(response.data));
+ })
+ .catch((e: any) => {
+ setTrackingPurchase(false);
+ setTrackPurchaseResponse(JSON.stringify(e));
+ });
+ } catch (error) {
+ setTrackingPurchase(false);
+ setTrackPurchaseResponse(JSON.stringify(error.message));
+ }
+ }
+ };
+
+ const handleUpdateUser = (e: FormEvent) => {
+ e.preventDefault();
+ const jsonObj = handleParseUserJson();
+ if (jsonObj) {
+ setUpdatingUser(true);
+ try {
+ updateUser(jsonObj)
+ .then((response: any) => {
+ setUpdateUserResponse(JSON.stringify(response.data));
+ setUpdatingUser(false);
+ })
+ .catch((e: any) => {
+ setUpdateUserResponse(JSON.stringify(e));
+ setUpdatingUser(false);
+ });
+ } catch (error) {
+ setUpdateUserResponse(JSON.stringify(error.message));
+ setUpdatingUser(false);
+ }
+ }
+ };
+
+ const handleParseTrackJson = () => {
+ try {
+ // Parse JSON and assert its type
+ const parsedObject = JSON.parse(trackEvent) as InAppTrackRequestParams;
+ return parsedObject;
+ } catch (error) {
+ setTrackResponse(JSON.stringify(error.message));
+ return error;
+ }
+ };
+
+ const handleTrack = async (e: FormEvent) => {
+ e.preventDefault();
+ setTrackingEvent(true);
+
+ const jsonObj = handleParseTrackJson();
+ if (jsonObj) {
+ const conditionalParams = jsonObj;
+
+ try {
+ track({
+ ...conditionalParams,
+ deviceInfo: {
+ appPackageName: 'my-website'
+ }
+ })
+ .then((response: any) => {
+ setTrackResponse(JSON.stringify(response.data));
+ setTrackingEvent(false);
+ })
+ .catch((e: any) => {
+ if (e && e.response && e.response.data) {
+ setTrackResponse(JSON.stringify(e.response.data));
+ } else {
+ setTrackResponse(JSON.stringify(e));
+ }
+ setTrackingEvent(false);
+ });
+ } catch (error) {
+ setTrackResponse(JSON.stringify(error.message));
+ setTrackingEvent(false);
+ }
+ }
+ };
+
+ const formAttr = { 'data-qa-track-submit': true };
+ const inputAttr = { 'data-qa-track-input': true };
+ const responseAttr = { 'data-qa-track-response': true };
+
+ const acceptCookie = () => setConsent(true);
+
+ const declineCookie = () => setConsent(false);
+
+ const renderCookieConsent = setConsent && (
+
+
We value your privacy
+
+ We use cookies to enhance your browsing experience, serve personalized
+ ads or content, and analyze our traffic. By clicking "Accept",
+ you consent to our use of cookies.
+
+
+
+
+
+
+ );
+
+ return (
+ <>
+
Commerce Endpoints
+ POST /updateCart
+
+
+ {updateCartResponse}
+
+ POST /trackPurchase
+
+
+ {trackPurchaseResponse}
+
+
User Endpoint
+ POST /users/update
+
+
+ {updateUserResponse}
+
+