feat: add analytics tracking to key components

- Track tab creation and closure events in TabManager
- Add session tracking (created, resumed, completed) in ClaudeCodeSession
- Track model selection changes in ClaudeCodeSession
- Monitor agent execution events (success/failure) in AgentExecution
- Include execution duration metrics for agents
- Use useTrackEvent hook for consistent event tracking
This commit is contained in:
Vivek R
2025-07-30 19:45:00 +05:30
parent 77e0ef0e73
commit f7e932ed79
3 changed files with 40 additions and 0 deletions

View File

@@ -36,6 +36,7 @@ import { ErrorBoundary } from "./ErrorBoundary";
import { useVirtualizer } from "@tanstack/react-virtual"; import { useVirtualizer } from "@tanstack/react-virtual";
import { AGENT_ICONS } from "./CCAgents"; import { AGENT_ICONS } from "./CCAgents";
import { HooksEditor } from "./HooksEditor"; import { HooksEditor } from "./HooksEditor";
import { useTrackEvent, useComponentMetrics } from "@/hooks";
interface AgentExecutionProps { interface AgentExecutionProps {
/** /**
@@ -89,6 +90,10 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); const [copyPopoverOpen, setCopyPopoverOpen] = useState(false);
// Analytics tracking
const trackEvent = useTrackEvent();
useComponentMetrics('AgentExecution');
// Hooks configuration state // Hooks configuration state
const [isHooksDialogOpen, setIsHooksDialogOpen] = useState(false); const [isHooksDialogOpen, setIsHooksDialogOpen] = useState(false);
const [activeHooksTab, setActiveHooksTab] = useState("project"); const [activeHooksTab, setActiveHooksTab] = useState("project");
@@ -302,6 +307,9 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
console.log("Agent execution started with run ID:", executionRunId); console.log("Agent execution started with run ID:", executionRunId);
setRunId(executionRunId); setRunId(executionRunId);
// Track agent execution start
trackEvent.agentExecuted(agent.name || 'custom', true, agent.name, undefined);
// Set up event listeners with run ID isolation // Set up event listeners with run ID isolation
const outputUnlisten = await listen<string>(`agent-output:${executionRunId}`, (event) => { const outputUnlisten = await listen<string>(`agent-output:${executionRunId}`, (event) => {
try { try {
@@ -323,9 +331,13 @@ export const AgentExecution: React.FC<AgentExecutionProps> = ({
const completeUnlisten = await listen<boolean>(`agent-complete:${executionRunId}`, (event) => { const completeUnlisten = await listen<boolean>(`agent-complete:${executionRunId}`, (event) => {
setIsRunning(false); setIsRunning(false);
const duration = executionStartTime ? Date.now() - executionStartTime : undefined;
setExecutionStartTime(null); setExecutionStartTime(null);
if (!event.payload) { if (!event.payload) {
setError("Agent execution failed"); setError("Agent execution failed");
trackEvent.agentExecuted(agent.name || 'custom', false, agent.name, duration);
} else {
trackEvent.agentExecuted(agent.name || 'custom', true, agent.name, duration);
} }
}); });

View File

@@ -33,6 +33,7 @@ import { SplitPane } from "@/components/ui/split-pane";
import { WebviewPreview } from "./WebviewPreview"; import { WebviewPreview } from "./WebviewPreview";
import type { ClaudeStreamMessage } from "./AgentExecution"; import type { ClaudeStreamMessage } from "./AgentExecution";
import { useVirtualizer } from "@tanstack/react-virtual"; import { useVirtualizer } from "@tanstack/react-virtual";
import { useTrackEvent, useComponentMetrics } from "@/hooks";
interface ClaudeCodeSessionProps { interface ClaudeCodeSessionProps {
/** /**
@@ -114,6 +115,10 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
const isMountedRef = useRef(true); const isMountedRef = useRef(true);
const isListeningRef = useRef(false); const isListeningRef = useRef(false);
// Analytics tracking
const trackEvent = useTrackEvent();
useComponentMetrics('ClaudeCodeSession');
// Keep ref in sync with state // Keep ref in sync with state
useEffect(() => { useEffect(() => {
queuedPromptsRef.current = queuedPrompts; queuedPromptsRef.current = queuedPrompts;
@@ -595,10 +600,14 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
// Execute the appropriate command // Execute the appropriate command
if (effectiveSession && !isFirstPrompt) { if (effectiveSession && !isFirstPrompt) {
console.log('[ClaudeCodeSession] Resuming session:', effectiveSession.id); console.log('[ClaudeCodeSession] Resuming session:', effectiveSession.id);
trackEvent.sessionResumed(effectiveSession.id);
trackEvent.modelSelected(model);
await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model); await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model);
} else { } else {
console.log('[ClaudeCodeSession] Starting new session'); console.log('[ClaudeCodeSession] Starting new session');
setIsFirstPrompt(false); setIsFirstPrompt(false);
trackEvent.sessionCreated(model, 'prompt_input');
trackEvent.modelSelected(model);
await api.executeClaudeCode(projectPath, prompt, model); await api.executeClaudeCode(projectPath, prompt, model);
} }
} }
@@ -821,6 +830,11 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
isMountedRef.current = false; isMountedRef.current = false;
isListeningRef.current = false; isListeningRef.current = false;
// Track session completion
if (effectiveSession) {
trackEvent.sessionCompleted();
}
// Clean up listeners // Clean up listeners
unlistenRefs.current.forEach(unlisten => unlisten()); unlistenRefs.current.forEach(unlisten => unlisten());
unlistenRefs.current = []; unlistenRefs.current = [];

View File

@@ -4,6 +4,7 @@ import { X, Plus, MessageSquare, Bot, AlertCircle, Loader2, Folder, BarChart, Se
import { useTabState } from '@/hooks/useTabState'; import { useTabState } from '@/hooks/useTabState';
import { Tab } from '@/contexts/TabContext'; import { Tab } from '@/contexts/TabContext';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { useTrackEvent } from '@/hooks';
interface TabItemProps { interface TabItemProps {
tab: Tab; tab: Tab;
@@ -133,6 +134,9 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
const [showLeftScroll, setShowLeftScroll] = useState(false); const [showLeftScroll, setShowLeftScroll] = useState(false);
const [showRightScroll, setShowRightScroll] = useState(false); const [showRightScroll, setShowRightScroll] = useState(false);
// Analytics tracking
const trackEvent = useTrackEvent();
// Listen for tab switch events // Listen for tab switch events
useEffect(() => { useEffect(() => {
@@ -151,10 +155,15 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
useEffect(() => { useEffect(() => {
const handleCreateTab = () => { const handleCreateTab = () => {
createChatTab(); createChatTab();
trackEvent.tabCreated('chat');
}; };
const handleCloseTab = async () => { const handleCloseTab = async () => {
if (activeTabId) { if (activeTabId) {
const tab = tabs.find(t => t.id === activeTabId);
if (tab) {
trackEvent.tabClosed(tab.type);
}
await closeTab(activeTabId); await closeTab(activeTabId);
} }
}; };
@@ -227,12 +236,17 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
}; };
const handleCloseTab = async (id: string) => { const handleCloseTab = async (id: string) => {
const tab = tabs.find(t => t.id === id);
if (tab) {
trackEvent.tabClosed(tab.type);
}
await closeTab(id); await closeTab(id);
}; };
const handleNewTab = () => { const handleNewTab = () => {
if (canAddTab()) { if (canAddTab()) {
createProjectsTab(); createProjectsTab();
trackEvent.tabCreated('projects');
} }
}; };