diff --git a/package.json b/package.json
index a5499614..2c644a07 100755
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
"match-sorter": "6.3.0",
"moment": "^2.29.1",
"nouislider": "15.0.0",
+ "posthog-js": "^1.281.0",
"react": "18.3.1",
"react-apexcharts": "^1.3.9",
"react-big-calendar": "^0.33.2",
diff --git a/src/App.js b/src/App.js
index 64e84621..362942f4 100755
--- a/src/App.js
+++ b/src/App.js
@@ -59,6 +59,7 @@ import NotificationContainer from "components/NotificationContainer";
import ConnectionStatusBar from "components/ConnectionStatusBar";
import NotificationTestTool from "components/NotificationTestTool";
import ScrollToTop from "components/ScrollToTop";
+import PostHogProvider from "components/PostHogProvider";
import { logger } from "utils/logger";
/**
@@ -295,32 +296,34 @@ export default function App() {
}, []);
return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
);
}
\ No newline at end of file
diff --git a/src/components/PostHogProvider.js b/src/components/PostHogProvider.js
new file mode 100644
index 00000000..0ed48309
--- /dev/null
+++ b/src/components/PostHogProvider.js
@@ -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:
+ *
+ *
+ *
+ */
+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;
diff --git a/src/hooks/usePageTracking.js b/src/hooks/usePageTracking.js
new file mode 100644
index 00000000..6a91ff6e
--- /dev/null
+++ b/src/hooks/usePageTracking.js
@@ -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;
diff --git a/src/hooks/usePostHog.js b/src/hooks/usePostHog.js
new file mode 100644
index 00000000..eb51f4e9
--- /dev/null
+++ b/src/hooks/usePostHog.js
@@ -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;
diff --git a/src/lib/constants.js b/src/lib/constants.js
new file mode 100644
index 00000000..ec47bd03
--- /dev/null
+++ b/src/lib/constants.js
@@ -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);
+};
diff --git a/src/lib/posthog.js b/src/lib/posthog.js
new file mode 100644
index 00000000..1173a250
--- /dev/null
+++ b/src/lib/posthog.js
@@ -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;