feat: add analytics React hooks
- Create useAnalytics hook for accessing analytics service - Add useTrackEvent hook with pre-built event tracking methods - Implement usePageView hook for automatic page tracking - Add useAppLifecycle hook for app start/close events - Create useComponentMetrics hook for component performance tracking - Add useInteractionTracking hook for user interaction events - Export all analytics hooks from hooks index
This commit is contained in:
@@ -3,4 +3,12 @@ export { useLoadingState } from './useLoadingState';
|
|||||||
export { useDebounce, useDebouncedCallback } from './useDebounce';
|
export { useDebounce, useDebouncedCallback } from './useDebounce';
|
||||||
export { useApiCall } from './useApiCall';
|
export { useApiCall } from './useApiCall';
|
||||||
export { usePagination } from './usePagination';
|
export { usePagination } from './usePagination';
|
||||||
export { useTheme } from './useTheme';
|
export { useTheme } from './useTheme';
|
||||||
|
export {
|
||||||
|
useAnalytics,
|
||||||
|
useTrackEvent,
|
||||||
|
usePageView,
|
||||||
|
useAppLifecycle,
|
||||||
|
useComponentMetrics,
|
||||||
|
useInteractionTracking
|
||||||
|
} from './useAnalytics';
|
182
src/hooks/useAnalytics.ts
Normal file
182
src/hooks/useAnalytics.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
|
import { analytics, ANALYTICS_EVENTS, eventBuilders } from '@/lib/analytics';
|
||||||
|
import type { EventName } from '@/lib/analytics/types';
|
||||||
|
|
||||||
|
interface UseAnalyticsReturn {
|
||||||
|
track: (eventName: EventName | string, properties?: Record<string, any>) => void;
|
||||||
|
trackEvent: ReturnType<typeof useTrackEvent>;
|
||||||
|
isEnabled: boolean;
|
||||||
|
hasConsented: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAnalytics(): UseAnalyticsReturn {
|
||||||
|
const isEnabled = analytics.isEnabled();
|
||||||
|
const hasConsented = analytics.hasConsented();
|
||||||
|
|
||||||
|
const track = useCallback((eventName: EventName | string, properties?: Record<string, any>) => {
|
||||||
|
analytics.track(eventName, properties);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const trackEvent = useTrackEvent();
|
||||||
|
|
||||||
|
return {
|
||||||
|
track,
|
||||||
|
trackEvent,
|
||||||
|
isEnabled,
|
||||||
|
hasConsented,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useTrackEvent() {
|
||||||
|
return {
|
||||||
|
// Session events
|
||||||
|
sessionCreated: (model: string, source?: string) => {
|
||||||
|
const event = eventBuilders.session({ model, source });
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
sessionCompleted: () => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.SESSION_COMPLETED);
|
||||||
|
},
|
||||||
|
|
||||||
|
sessionResumed: (checkpointId: string) => {
|
||||||
|
const event = eventBuilders.session({ resumed: true, checkpoint_id: checkpointId });
|
||||||
|
analytics.track(ANALYTICS_EVENTS.SESSION_RESUMED, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Feature usage
|
||||||
|
featureUsed: (feature: string, subfeature?: string, metadata?: Record<string, any>) => {
|
||||||
|
const event = eventBuilders.feature(feature, subfeature, metadata);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Model selection
|
||||||
|
modelSelected: (newModel: string, previousModel?: string, source?: string) => {
|
||||||
|
const event = eventBuilders.model(newModel, previousModel, source);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Tab events
|
||||||
|
tabCreated: (tabType: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.TAB_CREATED, { tab_type: tabType });
|
||||||
|
},
|
||||||
|
|
||||||
|
tabClosed: (tabType: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.TAB_CLOSED, { tab_type: tabType });
|
||||||
|
},
|
||||||
|
|
||||||
|
// File operations
|
||||||
|
fileOpened: (fileType: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.FILE_OPENED, { file_type: fileType });
|
||||||
|
},
|
||||||
|
|
||||||
|
fileEdited: (fileType: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.FILE_EDITED, { file_type: fileType });
|
||||||
|
},
|
||||||
|
|
||||||
|
fileSaved: (fileType: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.FILE_SAVED, { file_type: fileType });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Agent execution
|
||||||
|
agentExecuted: (agentType: string, success: boolean, agentName?: string, durationMs?: number) => {
|
||||||
|
const event = eventBuilders.agent(agentType, success, agentName, durationMs);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// MCP events
|
||||||
|
mcpServerConnected: (serverName: string, success: boolean, serverType?: string) => {
|
||||||
|
const event = eventBuilders.mcp(serverName, success, serverType);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
mcpServerDisconnected: (serverName: string) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.MCP_SERVER_DISCONNECTED, { server_name: serverName });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Slash commands
|
||||||
|
slashCommandUsed: (command: string, success: boolean) => {
|
||||||
|
const event = eventBuilders.slashCommand(command, success);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
settingsChanged: (setting: string, value: any) => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.SETTINGS_CHANGED, { setting, value });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
errorOccurred: (errorType: string, errorCode?: string, context?: string) => {
|
||||||
|
const event = eventBuilders.error(errorType, errorCode, context);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Performance
|
||||||
|
performanceMetrics: (metrics: Record<string, number>) => {
|
||||||
|
const event = eventBuilders.performance(metrics);
|
||||||
|
analytics.track(event.event, event.properties);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePageView(pageName: string, properties?: Record<string, any>) {
|
||||||
|
const hasTracked = useRef(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!hasTracked.current && analytics.isEnabled()) {
|
||||||
|
analytics.track('page_view', {
|
||||||
|
page_name: pageName,
|
||||||
|
...properties,
|
||||||
|
});
|
||||||
|
hasTracked.current = true;
|
||||||
|
}
|
||||||
|
}, [pageName, properties]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAppLifecycle() {
|
||||||
|
useEffect(() => {
|
||||||
|
// Track app start
|
||||||
|
analytics.track(ANALYTICS_EVENTS.APP_STARTED);
|
||||||
|
|
||||||
|
// Track app close
|
||||||
|
const handleUnload = () => {
|
||||||
|
analytics.track(ANALYTICS_EVENTS.APP_CLOSED);
|
||||||
|
analytics.shutdown();
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('beforeunload', handleUnload);
|
||||||
|
return () => window.removeEventListener('beforeunload', handleUnload);
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook for tracking component-specific metrics
|
||||||
|
export function useComponentMetrics(componentName: string) {
|
||||||
|
const mountTime = useRef(Date.now());
|
||||||
|
const renderCount = useRef(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
renderCount.current += 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
// Track component unmount metrics
|
||||||
|
const lifetime = Date.now() - mountTime.current;
|
||||||
|
analytics.track('component_metrics', {
|
||||||
|
component: componentName,
|
||||||
|
lifetime_ms: lifetime,
|
||||||
|
render_count: renderCount.current,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [componentName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook for tracking user interactions
|
||||||
|
export function useInteractionTracking(interactionType: string) {
|
||||||
|
return useCallback((details?: Record<string, any>) => {
|
||||||
|
analytics.track('user_interaction', {
|
||||||
|
interaction_type: interactionType,
|
||||||
|
...details,
|
||||||
|
});
|
||||||
|
}, [interactionType]);
|
||||||
|
}
|
Reference in New Issue
Block a user