## Summary Closes #35. - **Flip deploy workflows** (mirror `dta_webapp`): push/merge to `master` → auto-deploy **beta**; **Deploy Prod** is `workflow_dispatch` only - **Tianji**: `REACT_APP_TIANJI_WEBSITE_ID` + `REACT_APP_DEPLOY_ENV` in `.env.production` / `.env.beta`; Tracker loads page views always on prod/beta - **Consent policy**: page views always live; custom events / identify gated via `analyticsConsent` + `AnalyticsConsentBanner` + `trackEvent`/`identifyUser` helpers (call sites remain #36) - Remove invalid Tianji `<script>` from `index.tsx` (Tracker component owns injection) - README documents beta auto / prod button + Tianji policy ## Ops note — beta Tianji website ID Prod ID unchanged: `cm7x7m52m03kbddswbswrt17y`. `.env.beta` has `REACT_APP_TIANJI_WEBSITE_ID=` empty until a **Hesychia Beta** website is created in Tianji (workspace `cm7w8087y020lddswyhamadj2`). Paste the new id into `.env.beta` before beta page views show under a distinct site. ## Test plan - [x] `npm run test:ci` (106 tests) - [ ] Create Hesychia Beta Tianji website; set `REACT_APP_TIANJI_WEBSITE_ID` in `.env.beta` - [ ] Merge to master → Deploy Beta runs; prod does **not** auto-deploy - [ ] Manual Run workflow on Deploy Prod still works - [ ] Beta build hits `beta.chatbackend` REST/WS; static root `/var/www/beta.chat.aimloperations/html` (8083) - [ ] Prod/beta: tracker.js loads without accepting consent; banner shows until Accept/Decline; `trackEvent` only fires after AcceptReviewed-on: #42
This commit was merged in pull request #42.
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
import { hasAnalyticsConsent, isAnalyticsEnvironment } from './analyticsConsent';
|
||||
|
||||
type QueuedCall =
|
||||
| { type: 'track'; eventName: string; data?: Record<string, unknown> }
|
||||
| { type: 'identify'; userInfo: Record<string, unknown> };
|
||||
|
||||
const queue: QueuedCall[] = [];
|
||||
|
||||
/** Named events for product analytics. Page views stay on automatic tracker.js. */
|
||||
export const AnalyticsEvents = {
|
||||
LOGIN_SUCCESS: 'Login Success',
|
||||
LOGIN_FAILED: 'Login Failed',
|
||||
SIGNUP_STARTED: 'Sign Up Started',
|
||||
SIGNUP_SUCCESS: 'Sign Up Success',
|
||||
SIGNUP_FAILED: 'Sign Up Failed',
|
||||
LOGOUT: 'Logout',
|
||||
CHECKOUT_STARTED: 'Checkout Started',
|
||||
PAYMENT_SUCCESS: 'Payment Success',
|
||||
PAYMENT_CANCELED: 'Payment Canceled',
|
||||
CONVERSATION_CREATED: 'Conversation Created',
|
||||
MESSAGE_SENT: 'Message Sent',
|
||||
TOS_ACKNOWLEDGED: 'ToS Acknowledged',
|
||||
BILLING_PORTAL_OPENED: 'Billing Portal Opened',
|
||||
} as const;
|
||||
|
||||
export type AnalyticsEventName = (typeof AnalyticsEvents)[keyof typeof AnalyticsEvents];
|
||||
|
||||
/** Custom events + identify require consent. Automatic page views do not. */
|
||||
const isEventTrackingEnabled = (): boolean =>
|
||||
isAnalyticsEnvironment() && hasAnalyticsConsent();
|
||||
|
||||
const runQueuedCall = (call: QueuedCall): void => {
|
||||
if (!window.tianji) return;
|
||||
|
||||
if (call.type === 'track') {
|
||||
window.tianji.track(call.eventName, call.data);
|
||||
return;
|
||||
}
|
||||
|
||||
window.tianji.identify(call.userInfo);
|
||||
};
|
||||
|
||||
export const flushAnalyticsQueue = (): void => {
|
||||
if (!isEventTrackingEnabled() || !window.tianji) return;
|
||||
|
||||
while (queue.length > 0) {
|
||||
const call = queue.shift();
|
||||
if (call) runQueuedCall(call);
|
||||
}
|
||||
};
|
||||
|
||||
export const trackEvent = (
|
||||
eventName: AnalyticsEventName | string,
|
||||
data?: Record<string, unknown>,
|
||||
): void => {
|
||||
if (!isEventTrackingEnabled()) return;
|
||||
|
||||
const payload = data ? { ...data } : undefined;
|
||||
|
||||
if (window.tianji?.track) {
|
||||
window.tianji.track(eventName, payload);
|
||||
return;
|
||||
}
|
||||
|
||||
queue.push({ type: 'track', eventName, data: payload });
|
||||
};
|
||||
|
||||
export const identifyUser = (userInfo: Record<string, unknown>): void => {
|
||||
if (!isEventTrackingEnabled()) return;
|
||||
|
||||
if (window.tianji?.identify) {
|
||||
window.tianji.identify(userInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
queue.push({ type: 'identify', userInfo: { ...userInfo } });
|
||||
};
|
||||
Reference in New Issue
Block a user