pango-zma-experience-content
v1.0.1
Published
A lightweight, reusable React component for integrating the Pango ZMA Experience Content into your application. ๐
Downloads
4
Readme
[Beta] PZEContent
A lightweight, reusable React component for integrating the Pango ZMA Experience Content into your application. ๐
Features
- ๐ Easy integration with any React project
- โก Fast setup with minimal configuration
- ๐ฌ Supports event callbacks:
authorize,followOA,followOaCustom,initSuccess,click-cta,openLink - ๐ฆ Published as ESM, CJS, and TypeScript-ready
Before Integration
- Contact PangoCDP guide setup (integrate batch voucher/qr campaign/digital gift,.. and customUI,...) and get
pzeKey
Prerequisites
- Node.js >= 18.x
- React >= 18.0.0
- React Router DOM >= 6.0.0
- Vite >= 3 (ZMA Required Vite 5.x)
Usage
Basic Example
1. pzeContent.page.tsx
import { PZEContent } from 'pango-zma-experience-content';
import React from 'react';
import PZEContentHandler from './pzeContent.handler';
interface IPZEContentPage {
}
const PZEContentPage: React.FC = ({ }: IPZEContentPage) => {
return (
<div
id="pzeContent"
className="relative z-[60]"
>
<PZEContent
pzeKey='pango_key'
pzeEnv='production'
pzeVersion='latest'
userAppId='your_userAppId'
/>
<PZEContentHandler />
</div>
);
};
export default PZEContentPage;2. pzeContent.handler.tsx
import { PZEContentStore, TPzeContent } from 'pango-zma-experience-content';
import React, { useEffect, useRef } from 'react';
import { getRecoil, setRecoil } from 'recoil-nexus';
import { followOA } from 'zmp-sdk/apis';
import { useIngestLog } from '~/hooks/useIngestLog';
import useShareUserInfo from '~/hooks/useShareUserInfo';
import { userInfoAtom } from '~/recoil/userInfo';
import useOpenLink from '~/hooks/useOpenLink';
interface IPZEContentHandler {
}
const PZEContentHandlerComponent: React.FC = ({ }: IPZEContentHandler) => {
const initializedRef = useRef(false);
const { handleActionSharePhoneAndLocation, handleActionSharePhone } = useShareUserInfo();
const { ingestLog } = useIngestLog()
const { handleOpenLink } = useOpenLink()
useEffect(() => {
if (initializedRef.current) {
return;
}
initializedRef.current = true;
PZEContentStore().then((atoms: TPzeContent) => {
if (!atoms.pzeContentStore) {
return;
}
// handle get current userInfo
const userInfo = getRecoil(userInfoAtom);
// handle updatepzeContentFollowedOA
atoms.updatepzeContentFollowedOA(userInfo?.followedOA);
// handle updatepzeContentUserInfo
atoms.updatepzeContentUserInfo({
userName: userInfo.userName == "Guest" ? "" : userInfo.userName,
userPhone: userInfo.userPhone,
userAvatar: "https://storage.googleapis.com/pangocdp-images/p-act/public/a446a8794d04498386b34dd833fb7eef.png",
});
const handlers = {
"zmp": {
"authorize": async () => {
try {
// handle call api zmp
// scope values: "userPhonenumber" | "userPhonenumber_userLocation"
// check scope and update to pzeContent
// + handleActionSharePhone
// + handleActionSharePhoneAndLocation
if (!scope) {
return;
}
const userInfoUpdate: any = scope === "userPhonenumber"
? await handleActionSharePhone()
: await handleActionSharePhoneAndLocation();
if (userInfoUpdate.userPhone && userInfoUpdate.userName) {
atoms.updatepzeContentUserInfo({
userName: userInfoUpdate.userName,
userPhone: userInfoUpdate.userPhone,
userAvatar: userInfoUpdate.userAvatar || "https://storage.googleapis.com/pangocdp-images/p-act/public/a446a8794d04498386b34dd833fb7eef.png",
});
return;
} else {
// handle close popup when fail
atoms.pzeContentClose();
return;
}
} catch (error: any) {
atoms.pzeContentClose();
}
},
"followOA": async () => {
try {
// handle followedOA will skip call zmp
if (getRecoil(userInfoAtom)?.followedOA) {
atoms.updatepzeContentFollowedOA(true);
return;
}
const oaId = userInfo.oaId;
if (!oaId) {
return;
}
await followOA({
id: oaId,
success: async () => {
// handle update current userInfo
const userInfoUpdate = { ...userInfo, followedOA: true }
setRecoil(userInfoAtom, userInfoUpdate)
// example: push event log follow-oa
await ingestLog(
'follow-oa',
{ userEvent: true },
userInfoUpdate
)
// handle updatepzeContentFollowedOA
atoms.updatepzeContentFollowedOA(true);
},
fail: (error: any) => {
atoms.pzeContentClose();
return;
},
});
} catch (error) {
atoms.pzeContentClose();
return;
}
},
"followOaCustom": async () => {
try {
// handle followedOACustom will skip call zmp
if (getRecoil(userInfoAtom)?.followedOACustom) {
atoms.updatepzeContentFollowedOACustom(true);
return;
}
const oaId = atoms.pzeContentStore.get(atoms.pzeCallback)?.data?.oaId;
if (!oaId) {
return;
}
// handle call followOA by oaIdCustom
} catch (error) {
atoms.pzeContentClose();
return;
}
}
},
"content": {
"click-cta": async () => {
const directLink = atoms.getPZECallbackData()?.data?.directLink;
// example push event log: click-cta leading to a specific directLink
},
"openLink": async () => {
const link = atoms.pzeContentStore.get(atoms.pzeCallback)?.data?.link;
handleOpenLink(link)
},
"initSuccess": async () => {
// example: push event log open-flow
// ingestLog(
// 'open-flow',
// {
// userEvent: false,
// "pze": "content",
// ...eventData
// },
// userInfo)
},
},
default: () => {
console.warn('Unknown message type: ' + atoms.getPZECallbackData()?.type);
},
};
const unsub = atoms.pzeContentStore.sub(atoms.pzeCallback, () => {
const callbackData = atoms.getPZECallbackData();
console.log("๐ฌ ", callbackData)
const type = callbackData?.type;
const action = callbackData?.data?.action;
if (!type || !action) {
return;
}
(handlers?.[type]?.[action] || handlers.default)();
});
return () => unsub?.();
});
}, []);
return null;
};
const PZEContentHandler = React.memo(PZEContentHandlerComponent);
export default PZEContentHandler;