feat(analytics): integrate analytics across remaining UI components

- Add slash command tracking:
  - Track command selection with method (click/keyboard/autocomplete)
  - Monitor command execution with parameters and timing
  - Record custom command creation events
- Enhance agent execution tracking:
  - Track agent lifecycle (start, progress, completion)
  - Monitor agent errors with retry context
  - Record execution duration and success metrics
- Add tab management analytics:
  - Track tab creation and closure events
  - Monitor active tab switches
- Implement timeline navigation tracking:
  - Track checkpoint navigation events
  - Monitor timeline interactions
- Update useAnalytics hook with comprehensive event helpers
- Export performance monitoring hooks from central index

This completes analytics integration across all major UI components
for full user interaction visibility.
This commit is contained in:
Vivek R
2025-07-31 14:22:47 +05:30
parent f08764c6ea
commit db1efc2831
7 changed files with 584 additions and 8 deletions

View File

@@ -2,6 +2,21 @@ import { useCallback, useEffect, useRef } from 'react';
import { analytics, ANALYTICS_EVENTS, eventBuilders } from '@/lib/analytics';
import type { EventName } from '@/lib/analytics/types';
// Screen name mapping for tab types
const TAB_SCREEN_NAMES: Record<string, string> = {
'chat': 'chat_session',
'agent': 'agent_view',
'projects': 'projects_list',
'usage': 'usage_dashboard',
'mcp': 'mcp_manager',
'settings': 'settings',
'claude-md': 'markdown_editor',
'claude-file': 'file_editor',
'agent-execution': 'agent_execution',
'create-agent': 'create_agent',
'import-agent': 'import_agent',
};
interface UseAnalyticsReturn {
track: (eventName: EventName | string, properties?: Record<string, any>) => void;
trackEvent: ReturnType<typeof useTrackEvent>;
@@ -116,6 +131,225 @@ export function useTrackEvent() {
const event = eventBuilders.performance(metrics);
analytics.track(event.event, event.properties);
},
// Claude Code Session events
promptSubmitted: (props: Parameters<typeof eventBuilders.promptSubmitted>[0]) => {
const event = eventBuilders.promptSubmitted(props);
analytics.track(event.event, event.properties);
},
sessionStopped: (props: Parameters<typeof eventBuilders.sessionStopped>[0]) => {
const event = eventBuilders.sessionStopped(props);
analytics.track(event.event, event.properties);
},
enhancedSessionStopped: (props: Parameters<typeof eventBuilders.enhancedSessionStopped>[0]) => {
const event = eventBuilders.enhancedSessionStopped(props);
analytics.track(event.event, event.properties);
},
checkpointCreated: (props: Parameters<typeof eventBuilders.checkpointCreated>[0]) => {
const event = eventBuilders.checkpointCreated(props);
analytics.track(event.event, event.properties);
},
checkpointRestored: (props: Parameters<typeof eventBuilders.checkpointRestored>[0]) => {
const event = eventBuilders.checkpointRestored(props);
analytics.track(event.event, event.properties);
},
toolExecuted: (props: Parameters<typeof eventBuilders.toolExecuted>[0]) => {
const event = eventBuilders.toolExecuted(props);
analytics.track(event.event, event.properties);
},
// Enhanced Agent events
agentStarted: (props: Parameters<typeof eventBuilders.agentStarted>[0]) => {
const event = eventBuilders.agentStarted(props);
analytics.track(event.event, event.properties);
},
agentProgress: (props: Parameters<typeof eventBuilders.agentProgress>[0]) => {
const event = eventBuilders.agentProgress(props);
analytics.track(event.event, event.properties);
},
agentError: (props: Parameters<typeof eventBuilders.agentError>[0]) => {
const event = eventBuilders.agentError(props);
analytics.track(event.event, event.properties);
},
// MCP events
mcpServerAdded: (props: Parameters<typeof eventBuilders.mcpServerAdded>[0]) => {
const event = eventBuilders.mcpServerAdded(props);
analytics.track(event.event, event.properties);
},
mcpServerRemoved: (props: Parameters<typeof eventBuilders.mcpServerRemoved>[0]) => {
const event = eventBuilders.mcpServerRemoved(props);
analytics.track(event.event, event.properties);
},
mcpToolInvoked: (props: Parameters<typeof eventBuilders.mcpToolInvoked>[0]) => {
const event = eventBuilders.mcpToolInvoked(props);
analytics.track(event.event, event.properties);
},
mcpConnectionError: (props: Parameters<typeof eventBuilders.mcpConnectionError>[0]) => {
const event = eventBuilders.mcpConnectionError(props);
analytics.track(event.event, event.properties);
},
// Slash Command events
slashCommandSelected: (props: Parameters<typeof eventBuilders.slashCommandSelected>[0]) => {
const event = eventBuilders.slashCommandSelected(props);
analytics.track(event.event, event.properties);
},
slashCommandExecuted: (props: Parameters<typeof eventBuilders.slashCommandExecuted>[0]) => {
const event = eventBuilders.slashCommandExecuted(props);
analytics.track(event.event, event.properties);
},
slashCommandCreated: (props: Parameters<typeof eventBuilders.slashCommandCreated>[0]) => {
const event = eventBuilders.slashCommandCreated(props);
analytics.track(event.event, event.properties);
},
// Error and Performance events
apiError: (props: Parameters<typeof eventBuilders.apiError>[0]) => {
const event = eventBuilders.apiError(props);
analytics.track(event.event, event.properties);
},
uiError: (props: Parameters<typeof eventBuilders.uiError>[0]) => {
const event = eventBuilders.uiError(props);
analytics.track(event.event, event.properties);
},
performanceBottleneck: (props: Parameters<typeof eventBuilders.performanceBottleneck>[0]) => {
const event = eventBuilders.performanceBottleneck(props);
analytics.track(event.event, event.properties);
},
memoryWarning: (props: Parameters<typeof eventBuilders.memoryWarning>[0]) => {
const event = eventBuilders.memoryWarning(props);
analytics.track(event.event, event.properties);
},
// User journey events
journeyMilestone: (props: Parameters<typeof eventBuilders.journeyMilestone>[0]) => {
const event = eventBuilders.journeyMilestone(props);
analytics.track(event.event, event.properties);
},
// Enhanced tracking methods
enhancedPromptSubmitted: (props: Parameters<typeof eventBuilders.enhancedPromptSubmitted>[0]) => {
const event = eventBuilders.enhancedPromptSubmitted(props);
analytics.track(event.event, event.properties);
},
enhancedToolExecuted: (props: Parameters<typeof eventBuilders.enhancedToolExecuted>[0]) => {
const event = eventBuilders.enhancedToolExecuted(props);
analytics.track(event.event, event.properties);
},
enhancedError: (props: Parameters<typeof eventBuilders.enhancedError>[0]) => {
const event = eventBuilders.enhancedError(props);
analytics.track(event.event, event.properties);
},
// Session engagement
sessionEngagement: (props: Parameters<typeof eventBuilders.sessionEngagement>[0]) => {
const event = eventBuilders.sessionEngagement(props);
analytics.track(event.event, event.properties);
},
// Feature discovery and adoption
featureDiscovered: (props: Parameters<typeof eventBuilders.featureDiscovered>[0]) => {
const event = eventBuilders.featureDiscovered(props);
analytics.track(event.event, event.properties);
},
featureAdopted: (props: Parameters<typeof eventBuilders.featureAdopted>[0]) => {
const event = eventBuilders.featureAdopted(props);
analytics.track(event.event, event.properties);
},
featureCombination: (props: Parameters<typeof eventBuilders.featureCombination>[0]) => {
const event = eventBuilders.featureCombination(props);
analytics.track(event.event, event.properties);
},
// Quality metrics
outputRegenerated: (props: Parameters<typeof eventBuilders.outputRegenerated>[0]) => {
const event = eventBuilders.outputRegenerated(props);
analytics.track(event.event, event.properties);
},
conversationAbandoned: (reason: string, messagesCount: number) => {
const event = eventBuilders.conversationAbandoned(reason, messagesCount);
analytics.track(event.event, event.properties);
},
suggestionAccepted: (props: Parameters<typeof eventBuilders.suggestionAccepted>[0]) => {
const event = eventBuilders.suggestionAccepted(props);
analytics.track(event.event, event.properties);
},
suggestionRejected: (props: Parameters<typeof eventBuilders.suggestionRejected>[0]) => {
const event = eventBuilders.suggestionRejected(props);
analytics.track(event.event, event.properties);
},
// AI interactions
aiInteraction: (props: Parameters<typeof eventBuilders.aiInteraction>[0]) => {
const event = eventBuilders.aiInteraction(props);
analytics.track(event.event, event.properties);
},
promptPattern: (props: Parameters<typeof eventBuilders.promptPattern>[0]) => {
const event = eventBuilders.promptPattern(props);
analytics.track(event.event, event.properties);
},
// Workflow tracking
workflowStarted: (props: Parameters<typeof eventBuilders.workflowStarted>[0]) => {
const event = eventBuilders.workflowStarted(props);
analytics.track(event.event, event.properties);
},
workflowCompleted: (props: Parameters<typeof eventBuilders.workflowCompleted>[0]) => {
const event = eventBuilders.workflowCompleted(props);
analytics.track(event.event, event.properties);
},
workflowAbandoned: (props: Parameters<typeof eventBuilders.workflowAbandoned>[0]) => {
const event = eventBuilders.workflowAbandoned(props);
analytics.track(event.event, event.properties);
},
// Network performance
networkPerformance: (props: Parameters<typeof eventBuilders.networkPerformance>[0]) => {
const event = eventBuilders.networkPerformance(props);
analytics.track(event.event, event.properties);
},
networkFailure: (props: Parameters<typeof eventBuilders.networkFailure>[0]) => {
const event = eventBuilders.networkFailure(props);
analytics.track(event.event, event.properties);
},
// Resource usage (direct methods)
resourceUsageHigh: (props: Parameters<typeof eventBuilders.resourceUsageHigh>[0]) => {
const event = eventBuilders.resourceUsageHigh(props);
analytics.track(event.event, event.properties);
},
resourceUsageSampled: (props: Parameters<typeof eventBuilders.resourceUsageSampled>[0]) => {
const event = eventBuilders.resourceUsageSampled(props);
analytics.track(event.event, event.properties);
},
};
}
@@ -124,7 +358,7 @@ export function usePageView(pageName: string, properties?: Record<string, any>)
useEffect(() => {
if (!hasTracked.current && analytics.isEnabled()) {
analytics.track('page_view', {
analytics.track('$pageview', {
page_name: pageName,
...properties,
});
@@ -179,4 +413,249 @@ export function useInteractionTracking(interactionType: string) {
...details,
});
}, [interactionType]);
}
}
// Hook for tracking screen changes
export function useScreenTracking(tabType?: string, tabId?: string) {
useEffect(() => {
if (tabType) {
const screenName = TAB_SCREEN_NAMES[tabType] || tabType;
const screenContext = tabId
? `${screenName}/${tabId.substring(0, 8)}`
: screenName;
analytics.setScreen(screenContext);
}
}, [tabType, tabId]);
}
// Export screen names for external use
export { TAB_SCREEN_NAMES };
// Hook for tracking feature experiments
export function useFeatureExperiment(featureName: string, variant: string) {
// const trackEvent = useTrackEvent();
useEffect(() => {
analytics.track('experiment_exposure', {
experiment_name: featureName,
variant,
exposure_time: Date.now(),
});
}, [featureName, variant]);
const trackConversion = useCallback((conversionType: string) => {
analytics.track('experiment_conversion', {
experiment_name: featureName,
variant,
conversion_type: conversionType,
});
}, [featureName, variant]);
return { trackConversion };
}
// Hook for tracking user paths/navigation
export function usePathTracking(pathname: string) {
const previousPath = useRef<string>('');
useEffect(() => {
if (previousPath.current && previousPath.current !== pathname) {
analytics.track('path_transition', {
from: previousPath.current,
to: pathname,
transition_type: 'navigation',
});
}
previousPath.current = pathname;
}, [pathname]);
}
// Hook for tracking feature adoption
export function useFeatureAdoptionTracking(featureName: string) {
const startTime = useRef<number>(Date.now());
const usageCount = useRef<number>(0);
const trackEvent = useTrackEvent();
const trackUsage = useCallback(() => {
usageCount.current += 1;
// Track discovery on first use
if (usageCount.current === 1) {
trackEvent.featureDiscovered({
feature_name: featureName,
discovery_method: 'organic',
time_to_first_use_ms: Date.now() - startTime.current,
initial_success: true,
});
}
// Track adoption after 5 uses
if (usageCount.current === 5) {
const daysSinceFirst = (Date.now() - startTime.current) / (1000 * 60 * 60 * 24);
trackEvent.featureAdopted({
feature: featureName,
adoption_stage: 'adopted',
usage_count: usageCount.current,
days_since_first_use: daysSinceFirst,
usage_trend: 'increasing',
});
}
}, [featureName, trackEvent]);
return { trackUsage, usageCount: usageCount.current };
}
// Hook for tracking workflow completion
export function useWorkflowTracking(workflowType: string) {
const startTime = useRef<number | null>(null);
const stepsCompleted = useRef<number>(0);
const toolsUsed = useRef<Set<string>>(new Set());
const interruptions = useRef<number>(0);
const trackEvent = useTrackEvent();
const startWorkflow = useCallback((totalSteps: number) => {
startTime.current = Date.now();
stepsCompleted.current = 0;
toolsUsed.current.clear();
interruptions.current = 0;
trackEvent.workflowStarted({
workflow_type: workflowType,
steps_completed: 0,
total_steps: totalSteps,
duration_ms: 0,
interruptions: 0,
completion_rate: 0,
tools_used: [],
});
}, [workflowType, trackEvent]);
const trackStep = useCallback((toolName?: string) => {
stepsCompleted.current += 1;
if (toolName) {
toolsUsed.current.add(toolName);
}
}, []);
const trackInterruption = useCallback(() => {
interruptions.current += 1;
}, []);
const completeWorkflow = useCallback((totalSteps: number, success: boolean = true) => {
if (!startTime.current) return;
const duration = Date.now() - startTime.current;
const completionRate = stepsCompleted.current / totalSteps;
const eventData = {
workflow_type: workflowType,
steps_completed: stepsCompleted.current,
total_steps: totalSteps,
duration_ms: duration,
interruptions: interruptions.current,
completion_rate: completionRate,
tools_used: Array.from(toolsUsed.current),
};
if (success) {
trackEvent.workflowCompleted(eventData);
} else {
trackEvent.workflowAbandoned(eventData);
}
// Reset
startTime.current = null;
}, [workflowType, trackEvent]);
return {
startWorkflow,
trackStep,
trackInterruption,
completeWorkflow,
};
}
// Hook for tracking AI interaction quality
export function useAIInteractionTracking(model: string) {
const interactionStart = useRef<number | null>(null);
const contextSwitches = useRef<number>(0);
const clarificationRequests = useRef<number>(0);
const trackEvent = useTrackEvent();
const startInteraction = useCallback(() => {
interactionStart.current = Date.now();
contextSwitches.current = 0;
clarificationRequests.current = 0;
}, []);
const trackContextSwitch = useCallback(() => {
contextSwitches.current += 1;
}, []);
const trackClarificationRequest = useCallback(() => {
clarificationRequests.current += 1;
}, []);
const completeInteraction = useCallback((
requestTokens: number,
responseTokens: number,
qualityScore?: number
) => {
if (!interactionStart.current) return;
trackEvent.aiInteraction({
model,
request_tokens: requestTokens,
response_tokens: responseTokens,
response_quality_score: qualityScore,
context_switches: contextSwitches.current,
clarification_requests: clarificationRequests.current,
});
// Reset
interactionStart.current = null;
}, [model, trackEvent]);
return {
startInteraction,
trackContextSwitch,
trackClarificationRequest,
completeInteraction,
};
}
// Hook for tracking network performance
export function useNetworkPerformanceTracking() {
const trackEvent = useTrackEvent();
const trackRequest = useCallback((
_endpoint: string,
endpointType: 'mcp' | 'api' | 'webhook',
latency: number,
payloadSize: number,
success: boolean,
retryCount: number = 0
) => {
const connectionQuality: 'excellent' | 'good' | 'poor' =
latency < 100 ? 'excellent' :
latency < 500 ? 'good' : 'poor';
const eventData = {
endpoint_type: endpointType,
latency_ms: latency,
payload_size_bytes: payloadSize,
connection_quality: connectionQuality,
retry_count: retryCount,
circuit_breaker_triggered: false,
};
if (success) {
trackEvent.networkPerformance(eventData);
} else {
trackEvent.networkFailure(eventData);
}
}, [trackEvent]);
return { trackRequest };
}