feat: PostHog 集成\

1.  安装依赖: posthog-js@^1.280.1
  2.  创建核心文件:
    - src/lib/posthog.js - PostHog SDK 封装(271 行)
    - src/lib/constants.js - 事件常量定义(AARRR 框架)
    - src/hooks/usePostHog.js - PostHog React Hook
    - src/hooks/usePageTracking.js - 页面追踪 Hook
    - src/components/PostHogProvider.js - Provider 组件
  3.  集成到应用:
    - 修改 src/App.js,在最外层添加 <PostHogProvider>
    - 自动追踪所有页面浏览
  4.  配置环境变量:
    - 在 .env 添加 PostHog 配置项
    - REACT_APP_POSTHOG_KEY 留空,需要用户填写
  5.  创建文档: POSTHOG_INTEGRATION.md 包含完整的使用说明
This commit is contained in:
zdl
2025-10-28 20:09:21 +08:00
parent 542b20368e
commit 6506cb222b
7 changed files with 892 additions and 27 deletions

View File

@@ -43,6 +43,7 @@
"match-sorter": "6.3.0", "match-sorter": "6.3.0",
"moment": "^2.29.1", "moment": "^2.29.1",
"nouislider": "15.0.0", "nouislider": "15.0.0",
"posthog-js": "^1.281.0",
"react": "18.3.1", "react": "18.3.1",
"react-apexcharts": "^1.3.9", "react-apexcharts": "^1.3.9",
"react-big-calendar": "^0.33.2", "react-big-calendar": "^0.33.2",

View File

@@ -59,6 +59,7 @@ import NotificationContainer from "components/NotificationContainer";
import ConnectionStatusBar from "components/ConnectionStatusBar"; import ConnectionStatusBar from "components/ConnectionStatusBar";
import NotificationTestTool from "components/NotificationTestTool"; import NotificationTestTool from "components/NotificationTestTool";
import ScrollToTop from "components/ScrollToTop"; import ScrollToTop from "components/ScrollToTop";
import PostHogProvider from "components/PostHogProvider";
import { logger } from "utils/logger"; import { logger } from "utils/logger";
/** /**
@@ -295,6 +296,7 @@ export default function App() {
}, []); }, []);
return ( return (
<PostHogProvider>
<ReduxProvider store={store}> <ReduxProvider store={store}>
<ChakraProvider <ChakraProvider
theme={theme} theme={theme}
@@ -322,5 +324,6 @@ export default function App() {
</ErrorBoundary> </ErrorBoundary>
</ChakraProvider> </ChakraProvider>
</ReduxProvider> </ReduxProvider>
</PostHogProvider>
); );
} }

View File

@@ -0,0 +1,83 @@
// src/components/PostHogProvider.js
import React, { useEffect, useState } from 'react';
import { initPostHog } from '../lib/posthog';
import { usePageTracking } from '../hooks/usePageTracking';
/**
* PostHog Provider Component
* Initializes PostHog SDK and provides automatic page view tracking
*
* Usage:
* <PostHogProvider>
* <App />
* </PostHogProvider>
*/
export const PostHogProvider = ({ children }) => {
const [isInitialized, setIsInitialized] = useState(false);
// Initialize PostHog once when component mounts
useEffect(() => {
// Only run in browser
if (typeof window === 'undefined') return;
// Initialize PostHog
initPostHog();
setIsInitialized(true);
// Log initialization
if (process.env.NODE_ENV === 'development') {
console.log('✅ PostHogProvider initialized');
}
}, []);
// Automatically track page views
usePageTracking({
enabled: isInitialized,
getProperties: (location) => {
// Add custom properties based on route
const properties = {};
// Identify page type based on path
if (location.pathname === '/home' || location.pathname === '/home/') {
properties.page_type = 'landing';
} else if (location.pathname.startsWith('/home/center')) {
properties.page_type = 'dashboard';
} else if (location.pathname.startsWith('/auth/')) {
properties.page_type = 'auth';
} else if (location.pathname.startsWith('/community')) {
properties.page_type = 'feature';
properties.feature_name = 'community';
} else if (location.pathname.startsWith('/concepts')) {
properties.page_type = 'feature';
properties.feature_name = 'concepts';
} else if (location.pathname.startsWith('/stocks')) {
properties.page_type = 'feature';
properties.feature_name = 'stocks';
} else if (location.pathname.startsWith('/limit-analyse')) {
properties.page_type = 'feature';
properties.feature_name = 'limit_analyse';
} else if (location.pathname.startsWith('/trading-simulation')) {
properties.page_type = 'feature';
properties.feature_name = 'trading_simulation';
} else if (location.pathname.startsWith('/company')) {
properties.page_type = 'detail';
properties.content_type = 'company';
} else if (location.pathname.startsWith('/event-detail')) {
properties.page_type = 'detail';
properties.content_type = 'event';
}
return properties;
},
});
// Don't render children until PostHog is initialized
// This prevents tracking events before SDK is ready
if (!isInitialized) {
return children; // Or return a loading spinner
}
return <>{children}</>;
};
export default PostHogProvider;

View File

@@ -0,0 +1,55 @@
// src/hooks/usePageTracking.js
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import posthog from 'posthog-js';
/**
* Custom hook for automatic page view tracking with PostHog
*
* @param {Object} options - Configuration options
* @param {boolean} options.enabled - Whether tracking is enabled
* @param {Function} options.getProperties - Function to get custom properties for each page view
*/
export const usePageTracking = ({ enabled = true, getProperties } = {}) => {
const location = useLocation();
const previousPathRef = useRef('');
useEffect(() => {
if (!enabled) return;
// Get the current path
const currentPath = location.pathname + location.search;
// Skip if it's the same page (prevents duplicate tracking)
if (previousPathRef.current === currentPath) {
return;
}
// Update the previous path
previousPathRef.current = currentPath;
// Get custom properties if function provided
const customProperties = getProperties ? getProperties(location) : {};
// Track page view with PostHog
if (posthog && posthog.__loaded) {
posthog.capture('$pageview', {
$current_url: window.location.href,
path: location.pathname,
search: location.search,
hash: location.hash,
...customProperties,
});
// Log in development
if (process.env.NODE_ENV === 'development') {
console.log('📊 PostHog $pageview:', {
path: location.pathname,
...customProperties,
});
}
}
}, [location, enabled, getProperties]);
};
export default usePageTracking;

101
src/hooks/usePostHog.js Normal file
View File

@@ -0,0 +1,101 @@
// src/hooks/usePostHog.js
import { useCallback } from 'react';
import {
getPostHog,
trackEvent,
trackPageView,
identifyUser,
setUserProperties,
resetUser,
optIn,
optOut,
hasOptedOut,
getFeatureFlag,
isFeatureEnabled,
} from '../lib/posthog';
/**
* Custom hook to access PostHog functionality
* Provides convenient methods for tracking events and managing user sessions
*
* @returns {object} PostHog methods
*/
export const usePostHog = () => {
// Get PostHog instance
const posthog = getPostHog();
// Track custom event
const track = useCallback((eventName, properties = {}) => {
trackEvent(eventName, properties);
}, []);
// Track page view
const trackPage = useCallback((pagePath, properties = {}) => {
trackPageView(pagePath, properties);
}, []);
// Identify user
const identify = useCallback((userId, userProperties = {}) => {
identifyUser(userId, userProperties);
}, []);
// Set user properties
const setProperties = useCallback((properties) => {
setUserProperties(properties);
}, []);
// Reset user session (logout)
const reset = useCallback(() => {
resetUser();
}, []);
// Opt out of tracking
const optOutTracking = useCallback(() => {
optOut();
}, []);
// Opt in to tracking
const optInTracking = useCallback(() => {
optIn();
}, []);
// Check if user has opted out
const isOptedOut = useCallback(() => {
return hasOptedOut();
}, []);
// Get feature flag value
const getFlag = useCallback((flagKey, defaultValue = false) => {
return getFeatureFlag(flagKey, defaultValue);
}, []);
// Check if feature is enabled
const isEnabled = useCallback((flagKey) => {
return isFeatureEnabled(flagKey);
}, []);
return {
// Core PostHog instance
posthog,
// Tracking methods
track,
trackPage,
// User management
identify,
setProperties,
reset,
// Privacy controls
optOut: optOutTracking,
optIn: optInTracking,
isOptedOut,
// Feature flags
getFlag,
isEnabled,
};
};
export default usePostHog;

351
src/lib/constants.js Normal file
View File

@@ -0,0 +1,351 @@
// src/lib/constants.js
// PostHog Event Names and Constants
// Organized by AARRR Framework (Acquisition, Activation, Retention, Referral, Revenue)
// ============================================================================
// ACQUISITION (获客) - Landing page, marketing website events
// ============================================================================
export const ACQUISITION_EVENTS = {
// Landing page
LANDING_PAGE_VIEWED: 'Landing Page Viewed',
CTA_BUTTON_CLICKED: 'CTA Button Clicked',
FEATURE_CARD_VIEWED: 'Feature Card Viewed',
FEATURE_VIDEO_PLAYED: 'Feature Video Played',
// Pricing page
PRICING_PAGE_VIEWED: 'Pricing Page Viewed',
PRICING_PLAN_VIEWED: 'Pricing Plan Viewed',
PRICING_PLAN_SELECTED: 'Pricing Plan Selected',
// How to use page
HOW_TO_USE_PAGE_VIEWED: 'How To Use Page Viewed',
TUTORIAL_STEP_VIEWED: 'Tutorial Step Viewed',
// Roadmap page
ROADMAP_PAGE_VIEWED: 'Roadmap Page Viewed',
ROADMAP_ITEM_CLICKED: 'Roadmap Item Clicked',
};
// ============================================================================
// ACTIVATION (激活) - Sign up, login, onboarding
// ============================================================================
export const ACTIVATION_EVENTS = {
// Auth pages
LOGIN_PAGE_VIEWED: 'Login Page Viewed',
SIGNUP_PAGE_VIEWED: 'Signup Page Viewed',
// Login/Signup actions
LOGIN_METHOD_SELECTED: 'Login Method Selected', // wechat, email, phone
WECHAT_QR_DISPLAYED: 'WeChat QR Code Displayed',
WECHAT_QR_SCANNED: 'WeChat QR Code Scanned',
USER_LOGGED_IN: 'User Logged In',
USER_SIGNED_UP: 'User Signed Up',
VERIFICATION_CODE_SENT: 'Verification Code Sent',
VERIFICATION_CODE_SUBMITTED: 'Verification Code Submitted',
LOGIN_FAILED: 'Login Failed',
SIGNUP_FAILED: 'Signup Failed',
// Onboarding
ONBOARDING_STARTED: 'Onboarding Started',
ONBOARDING_STEP_COMPLETED: 'Onboarding Step Completed',
ONBOARDING_COMPLETED: 'Onboarding Completed',
ONBOARDING_SKIPPED: 'Onboarding Skipped',
// User agreement
USER_AGREEMENT_VIEWED: 'User Agreement Viewed',
USER_AGREEMENT_ACCEPTED: 'User Agreement Accepted',
PRIVACY_POLICY_VIEWED: 'Privacy Policy Viewed',
PRIVACY_POLICY_ACCEPTED: 'Privacy Policy Accepted',
};
// ============================================================================
// RETENTION (留存) - Core product usage, feature engagement
// ============================================================================
export const RETENTION_EVENTS = {
// Dashboard
DASHBOARD_VIEWED: 'Dashboard Viewed',
DASHBOARD_CENTER_VIEWED: 'Dashboard Center Viewed',
FUNCTION_CARD_CLICKED: 'Function Card Clicked', // Core功能卡片点击
// Navigation
TOP_NAV_CLICKED: 'Top Navigation Clicked',
SIDEBAR_MENU_CLICKED: 'Sidebar Menu Clicked',
MENU_ITEM_CLICKED: 'Menu Item Clicked',
BREADCRUMB_CLICKED: 'Breadcrumb Clicked',
// Search
SEARCH_INITIATED: 'Search Initiated',
SEARCH_QUERY_SUBMITTED: 'Search Query Submitted',
SEARCH_RESULT_CLICKED: 'Search Result Clicked',
SEARCH_NO_RESULTS: 'Search No Results',
SEARCH_FILTER_APPLIED: 'Search Filter Applied',
// News/Community (新闻催化分析)
COMMUNITY_PAGE_VIEWED: 'Community Page Viewed',
NEWS_LIST_VIEWED: 'News List Viewed',
NEWS_ARTICLE_CLICKED: 'News Article Clicked',
NEWS_DETAIL_OPENED: 'News Detail Opened',
NEWS_TAB_CLICKED: 'News Tab Clicked', // 相关标的, 相关概念, etc.
NEWS_FILTER_APPLIED: 'News Filter Applied',
NEWS_SORTED: 'News Sorted',
// Concept Center (概念中心)
CONCEPT_PAGE_VIEWED: 'Concept Page Viewed',
CONCEPT_LIST_VIEWED: 'Concept List Viewed',
CONCEPT_CLICKED: 'Concept Clicked',
CONCEPT_DETAIL_VIEWED: 'Concept Detail Viewed',
CONCEPT_STOCK_CLICKED: 'Concept Stock Clicked',
// Stock Center (个股中心)
STOCK_OVERVIEW_VIEWED: 'Stock Overview Page Viewed',
STOCK_LIST_VIEWED: 'Stock List Viewed',
STOCK_SEARCHED: 'Stock Searched',
STOCK_CLICKED: 'Stock Clicked',
STOCK_DETAIL_VIEWED: 'Stock Detail Viewed',
STOCK_TAB_CLICKED: 'Stock Tab Clicked', // 公司概览, 股票行情, 财务全景, 盈利预测
// Company Details
COMPANY_OVERVIEW_VIEWED: 'Company Overview Viewed',
COMPANY_FINANCIALS_VIEWED: 'Company Financials Viewed',
COMPANY_FORECAST_VIEWED: 'Company Forecast Viewed',
COMPANY_MARKET_DATA_VIEWED: 'Company Market Data Viewed',
// Limit Analysis (涨停分析)
LIMIT_ANALYSE_PAGE_VIEWED: 'Limit Analyse Page Viewed',
LIMIT_BOARD_CLICKED: 'Limit Board Clicked',
LIMIT_SECTOR_EXPANDED: 'Limit Sector Expanded',
LIMIT_SECTOR_ANALYSIS_VIEWED: 'Limit Sector Analysis Viewed',
LIMIT_STOCK_CLICKED: 'Limit Stock Clicked',
// Trading Simulation (模拟盘交易)
TRADING_SIMULATION_ENTERED: 'Trading Simulation Entered',
SIMULATION_ORDER_PLACED: 'Simulation Order Placed',
SIMULATION_HOLDINGS_VIEWED: 'Simulation Holdings Viewed',
SIMULATION_HISTORY_VIEWED: 'Simulation History Viewed',
SIMULATION_STOCK_SEARCHED: 'Simulation Stock Searched',
// Event Details
EVENT_DETAIL_VIEWED: 'Event Detail Viewed',
EVENT_ANALYSIS_VIEWED: 'Event Analysis Viewed',
EVENT_TIMELINE_CLICKED: 'Event Timeline Clicked',
// Profile & Settings
PROFILE_PAGE_VIEWED: 'Profile Page Viewed',
PROFILE_UPDATED: 'Profile Updated',
SETTINGS_PAGE_VIEWED: 'Settings Page Viewed',
SETTINGS_CHANGED: 'Settings Changed',
// Subscription Management
SUBSCRIPTION_PAGE_VIEWED: 'Subscription Page Viewed',
UPGRADE_PLAN_CLICKED: 'Upgrade Plan Clicked',
};
// ============================================================================
// REFERRAL (推荐) - Sharing, inviting
// ============================================================================
export const REFERRAL_EVENTS = {
// Sharing
SHARE_BUTTON_CLICKED: 'Share Button Clicked',
CONTENT_SHARED: 'Content Shared',
SHARE_LINK_GENERATED: 'Share Link Generated',
SHARE_MODAL_OPENED: 'Share Modal Opened',
SHARE_MODAL_CLOSED: 'Share Modal Closed',
// Referral
REFERRAL_PAGE_VIEWED: 'Referral Page Viewed',
REFERRAL_LINK_COPIED: 'Referral Link Copied',
REFERRAL_INVITE_SENT: 'Referral Invite Sent',
};
// ============================================================================
// REVENUE (收入) - Payment, subscription, monetization
// ============================================================================
export const REVENUE_EVENTS = {
// Paywall
PAYWALL_SHOWN: 'Paywall Shown',
PAYWALL_DISMISSED: 'Paywall Dismissed',
PAYWALL_UPGRADE_CLICKED: 'Paywall Upgrade Clicked',
// Payment
PAYMENT_PAGE_VIEWED: 'Payment Page Viewed',
PAYMENT_METHOD_SELECTED: 'Payment Method Selected',
PAYMENT_INITIATED: 'Payment Initiated',
PAYMENT_SUCCESSFUL: 'Payment Successful',
PAYMENT_FAILED: 'Payment Failed',
// Subscription
SUBSCRIPTION_CREATED: 'Subscription Created',
SUBSCRIPTION_RENEWED: 'Subscription Renewed',
SUBSCRIPTION_UPGRADED: 'Subscription Upgraded',
SUBSCRIPTION_DOWNGRADED: 'Subscription Downgraded',
SUBSCRIPTION_CANCELLED: 'Subscription Cancelled',
SUBSCRIPTION_EXPIRED: 'Subscription Expired',
// Refund
REFUND_REQUESTED: 'Refund Requested',
REFUND_PROCESSED: 'Refund Processed',
};
// ============================================================================
// SPECIAL EVENTS (特殊事件) - Errors, performance, chatbot
// ============================================================================
export const SPECIAL_EVENTS = {
// Errors
ERROR_OCCURRED: 'Error Occurred',
API_ERROR: 'API Error',
NOT_FOUND_404: '404 Not Found',
// Performance
PAGE_LOAD_TIME: 'Page Load Time',
API_RESPONSE_TIME: 'API Response Time',
// Chatbot (Dify)
CHATBOT_OPENED: 'Chatbot Opened',
CHATBOT_CLOSED: 'Chatbot Closed',
CHATBOT_MESSAGE_SENT: 'Chatbot Message Sent',
CHATBOT_MESSAGE_RECEIVED: 'Chatbot Message Received',
CHATBOT_FEEDBACK_PROVIDED: 'Chatbot Feedback Provided',
// Scroll depth
SCROLL_DEPTH_25: 'Scroll Depth 25%',
SCROLL_DEPTH_50: 'Scroll Depth 50%',
SCROLL_DEPTH_75: 'Scroll Depth 75%',
SCROLL_DEPTH_100: 'Scroll Depth 100%',
// Session
SESSION_STARTED: 'Session Started',
SESSION_ENDED: 'Session Ended',
USER_IDLE: 'User Idle',
USER_RETURNED: 'User Returned',
// Logout
USER_LOGGED_OUT: 'User Logged Out',
};
// ============================================================================
// USER PROPERTIES (用户属性)
// ============================================================================
export const USER_PROPERTIES = {
// Identity
EMAIL: 'email',
USERNAME: 'username',
USER_ID: 'user_id',
PHONE: 'phone',
// Subscription
SUBSCRIPTION_TIER: 'subscription_tier', // 'free', 'pro', 'enterprise'
SUBSCRIPTION_STATUS: 'subscription_status', // 'active', 'expired', 'cancelled'
SUBSCRIPTION_START_DATE: 'subscription_start_date',
SUBSCRIPTION_END_DATE: 'subscription_end_date',
// Engagement
REGISTRATION_DATE: 'registration_date',
LAST_LOGIN: 'last_login',
LOGIN_COUNT: 'login_count',
DAYS_SINCE_REGISTRATION: 'days_since_registration',
LIFETIME_VALUE: 'lifetime_value',
// Preferences
PREFERRED_LANGUAGE: 'preferred_language',
THEME_PREFERENCE: 'theme_preference', // 'light', 'dark'
NOTIFICATION_ENABLED: 'notification_enabled',
// Attribution
UTM_SOURCE: 'utm_source',
UTM_MEDIUM: 'utm_medium',
UTM_CAMPAIGN: 'utm_campaign',
REFERRER: 'referrer',
// Behavioral
FAVORITE_FEATURES: 'favorite_features',
MOST_VISITED_PAGES: 'most_visited_pages',
TOTAL_SESSIONS: 'total_sessions',
AVERAGE_SESSION_DURATION: 'average_session_duration',
};
// ============================================================================
// SUBSCRIPTION TIERS (订阅等级)
// ============================================================================
export const SUBSCRIPTION_TIERS = {
FREE: 'free',
PRO: 'pro',
ENTERPRISE: 'enterprise',
};
// ============================================================================
// PAGE TYPES (页面类型)
// ============================================================================
export const PAGE_TYPES = {
LANDING: 'landing',
DASHBOARD: 'dashboard',
FEATURE: 'feature',
DETAIL: 'detail',
AUTH: 'auth',
SETTINGS: 'settings',
PAYMENT: 'payment',
ERROR: 'error',
};
// ============================================================================
// CONTENT TYPES (内容类型)
// ============================================================================
export const CONTENT_TYPES = {
NEWS: 'news',
STOCK: 'stock',
CONCEPT: 'concept',
ANALYSIS: 'analysis',
EVENT: 'event',
COMPANY: 'company',
};
// ============================================================================
// SHARE CHANNELS (分享渠道)
// ============================================================================
export const SHARE_CHANNELS = {
WECHAT: 'wechat',
LINK: 'link',
QRCODE: 'qrcode',
EMAIL: 'email',
COPY: 'copy',
};
// ============================================================================
// LOGIN METHODS (登录方式)
// ============================================================================
export const LOGIN_METHODS = {
WECHAT: 'wechat',
EMAIL: 'email',
PHONE: 'phone',
USERNAME: 'username',
};
// ============================================================================
// PAYMENT METHODS (支付方式)
// ============================================================================
export const PAYMENT_METHODS = {
WECHAT_PAY: 'wechat_pay',
ALIPAY: 'alipay',
CREDIT_CARD: 'credit_card',
};
// ============================================================================
// Helper function to get all events
// ============================================================================
export const getAllEvents = () => {
return {
...ACQUISITION_EVENTS,
...ACTIVATION_EVENTS,
...RETENTION_EVENTS,
...REFERRAL_EVENTS,
...REVENUE_EVENTS,
...SPECIAL_EVENTS,
};
};
// ============================================================================
// Helper function to validate event name
// ============================================================================
export const isValidEvent = (eventName) => {
const allEvents = getAllEvents();
return Object.values(allEvents).includes(eventName);
};

271
src/lib/posthog.js Normal file
View File

@@ -0,0 +1,271 @@
// src/lib/posthog.js
import posthog from 'posthog-js';
/**
* Initialize PostHog SDK
* Should be called once when the app starts
*/
export const initPostHog = () => {
// Only run in browser environment
if (typeof window === 'undefined') return;
const apiKey = process.env.REACT_APP_POSTHOG_KEY;
const apiHost = process.env.REACT_APP_POSTHOG_HOST || 'https://app.posthog.com';
if (!apiKey) {
console.warn('⚠️ PostHog API key not found. Analytics will be disabled.');
return;
}
try {
posthog.init(apiKey, {
api_host: apiHost,
// Pageview tracking - manual control for better accuracy
capture_pageview: false, // We'll manually capture with custom properties
capture_pageleave: true, // Auto-capture when user leaves page
// Session Recording Configuration
session_recording: {
enabled: process.env.REACT_APP_ENABLE_SESSION_RECORDING === 'true',
// Privacy: Mask sensitive input fields
maskInputOptions: {
password: true,
email: true,
phone: true,
'data-sensitive': true, // Custom attribute for sensitive fields
},
// Record canvas for charts/graphs
recordCanvas: true,
// Network payload capture (useful for debugging API issues)
networkPayloadCapture: {
recordHeaders: true,
recordBody: true,
// Don't record sensitive endpoints
urlBlocklist: [
'/api/auth/session',
'/api/auth/login',
'/api/auth/register',
'/api/payment',
],
},
},
// Performance optimization
batch_size: 10, // Send events in batches of 10
batch_interval_ms: 3000, // Or every 3 seconds
// Privacy settings
respect_dnt: true, // Respect Do Not Track browser setting
persistence: 'localStorage+cookie', // Use both for reliability
// Feature flags (for A/B testing)
bootstrap: {
featureFlags: {},
},
// Autocapture settings
autocapture: {
// Automatically capture clicks on buttons, links, etc.
dom_event_allowlist: ['click', 'submit', 'change'],
// Capture additional element properties
capture_copied_text: false, // Don't capture copied text (privacy)
},
// Development debugging
loaded: (posthogInstance) => {
if (process.env.NODE_ENV === 'development') {
console.log('✅ PostHog initialized successfully');
posthogInstance.debug(); // Enable debug mode in development
}
},
});
console.log('📊 PostHog Analytics initialized');
} catch (error) {
console.error('❌ PostHog initialization failed:', error);
}
};
/**
* Get PostHog instance
* @returns {object} PostHog instance
*/
export const getPostHog = () => {
return posthog;
};
/**
* Identify user with PostHog
* Call this after successful login/registration
*
* @param {string} userId - Unique user identifier
* @param {object} userProperties - User properties (email, name, subscription_tier, etc.)
*/
export const identifyUser = (userId, userProperties = {}) => {
if (!userId) {
console.warn('⚠️ Cannot identify user: userId is required');
return;
}
try {
posthog.identify(userId, {
email: userProperties.email,
username: userProperties.username,
subscription_tier: userProperties.subscription_tier || 'free',
role: userProperties.role,
registration_date: userProperties.registration_date,
last_login: new Date().toISOString(),
...userProperties,
});
console.log('👤 User identified:', userId);
} catch (error) {
console.error('❌ User identification failed:', error);
}
};
/**
* Update user properties
* Use this to update user attributes without re-identifying
*
* @param {object} properties - Properties to update
*/
export const setUserProperties = (properties) => {
try {
posthog.people.set(properties);
console.log('📝 User properties updated');
} catch (error) {
console.error('❌ Failed to update user properties:', error);
}
};
/**
* Track custom event
*
* @param {string} eventName - Name of the event
* @param {object} properties - Event properties
*/
export const trackEvent = (eventName, properties = {}) => {
try {
posthog.capture(eventName, {
...properties,
timestamp: new Date().toISOString(),
});
if (process.env.NODE_ENV === 'development') {
console.log('📍 Event tracked:', eventName, properties);
}
} catch (error) {
console.error('❌ Event tracking failed:', error);
}
};
/**
* Track page view
*
* @param {string} pagePath - Current page path
* @param {object} properties - Additional properties
*/
export const trackPageView = (pagePath, properties = {}) => {
try {
posthog.capture('$pageview', {
$current_url: window.location.href,
page_path: pagePath,
page_title: document.title,
referrer: document.referrer,
...properties,
});
if (process.env.NODE_ENV === 'development') {
console.log('📄 Page view tracked:', pagePath);
}
} catch (error) {
console.error('❌ Page view tracking failed:', error);
}
};
/**
* Reset user session
* Call this on logout
*/
export const resetUser = () => {
try {
posthog.reset();
console.log('🔄 User session reset');
} catch (error) {
console.error('❌ Session reset failed:', error);
}
};
/**
* User opt-out from tracking
*/
export const optOut = () => {
try {
posthog.opt_out_capturing();
console.log('🚫 User opted out of tracking');
} catch (error) {
console.error('❌ Opt-out failed:', error);
}
};
/**
* User opt-in to tracking
*/
export const optIn = () => {
try {
posthog.opt_in_capturing();
console.log('✅ User opted in to tracking');
} catch (error) {
console.error('❌ Opt-in failed:', error);
}
};
/**
* Check if user has opted out
* @returns {boolean}
*/
export const hasOptedOut = () => {
try {
return posthog.has_opted_out_capturing();
} catch (error) {
console.error('❌ Failed to check opt-out status:', error);
return false;
}
};
/**
* Get feature flag value
* @param {string} flagKey - Feature flag key
* @param {any} defaultValue - Default value if flag not found
* @returns {any} Feature flag value
*/
export const getFeatureFlag = (flagKey, defaultValue = false) => {
try {
return posthog.getFeatureFlag(flagKey) || defaultValue;
} catch (error) {
console.error('❌ Failed to get feature flag:', error);
return defaultValue;
}
};
/**
* Check if feature flag is enabled
* @param {string} flagKey - Feature flag key
* @returns {boolean}
*/
export const isFeatureEnabled = (flagKey) => {
try {
return posthog.isFeatureEnabled(flagKey);
} catch (error) {
console.error('❌ Failed to check feature flag:', error);
return false;
}
};
export default posthog;