From f7e932ed79a9ea13d778c2ec967eb52615d8fd7b Mon Sep 17 00:00:00 2001 From: Vivek R <123vivekr@gmail.com> Date: Wed, 30 Jul 2025 19:45:00 +0530 Subject: [PATCH] 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 --- src/components/AgentExecution.tsx | 12 ++++++++++++ src/components/ClaudeCodeSession.tsx | 14 ++++++++++++++ src/components/TabManager.tsx | 14 ++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/src/components/AgentExecution.tsx b/src/components/AgentExecution.tsx index e267eaf..e312ef6 100644 --- a/src/components/AgentExecution.tsx +++ b/src/components/AgentExecution.tsx @@ -36,6 +36,7 @@ import { ErrorBoundary } from "./ErrorBoundary"; import { useVirtualizer } from "@tanstack/react-virtual"; import { AGENT_ICONS } from "./CCAgents"; import { HooksEditor } from "./HooksEditor"; +import { useTrackEvent, useComponentMetrics } from "@/hooks"; interface AgentExecutionProps { /** @@ -89,6 +90,10 @@ export const AgentExecution: React.FC = ({ const [error, setError] = useState(null); const [copyPopoverOpen, setCopyPopoverOpen] = useState(false); + // Analytics tracking + const trackEvent = useTrackEvent(); + useComponentMetrics('AgentExecution'); + // Hooks configuration state const [isHooksDialogOpen, setIsHooksDialogOpen] = useState(false); const [activeHooksTab, setActiveHooksTab] = useState("project"); @@ -302,6 +307,9 @@ export const AgentExecution: React.FC = ({ console.log("Agent execution started with run ID:", executionRunId); setRunId(executionRunId); + // Track agent execution start + trackEvent.agentExecuted(agent.name || 'custom', true, agent.name, undefined); + // Set up event listeners with run ID isolation const outputUnlisten = await listen(`agent-output:${executionRunId}`, (event) => { try { @@ -323,9 +331,13 @@ export const AgentExecution: React.FC = ({ const completeUnlisten = await listen(`agent-complete:${executionRunId}`, (event) => { setIsRunning(false); + const duration = executionStartTime ? Date.now() - executionStartTime : undefined; setExecutionStartTime(null); if (!event.payload) { setError("Agent execution failed"); + trackEvent.agentExecuted(agent.name || 'custom', false, agent.name, duration); + } else { + trackEvent.agentExecuted(agent.name || 'custom', true, agent.name, duration); } }); diff --git a/src/components/ClaudeCodeSession.tsx b/src/components/ClaudeCodeSession.tsx index 2dc9a9b..f66a68d 100644 --- a/src/components/ClaudeCodeSession.tsx +++ b/src/components/ClaudeCodeSession.tsx @@ -33,6 +33,7 @@ import { SplitPane } from "@/components/ui/split-pane"; import { WebviewPreview } from "./WebviewPreview"; import type { ClaudeStreamMessage } from "./AgentExecution"; import { useVirtualizer } from "@tanstack/react-virtual"; +import { useTrackEvent, useComponentMetrics } from "@/hooks"; interface ClaudeCodeSessionProps { /** @@ -114,6 +115,10 @@ export const ClaudeCodeSession: React.FC = ({ const isMountedRef = useRef(true); const isListeningRef = useRef(false); + // Analytics tracking + const trackEvent = useTrackEvent(); + useComponentMetrics('ClaudeCodeSession'); + // Keep ref in sync with state useEffect(() => { queuedPromptsRef.current = queuedPrompts; @@ -595,10 +600,14 @@ export const ClaudeCodeSession: React.FC = ({ // Execute the appropriate command if (effectiveSession && !isFirstPrompt) { console.log('[ClaudeCodeSession] Resuming session:', effectiveSession.id); + trackEvent.sessionResumed(effectiveSession.id); + trackEvent.modelSelected(model); await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model); } else { console.log('[ClaudeCodeSession] Starting new session'); setIsFirstPrompt(false); + trackEvent.sessionCreated(model, 'prompt_input'); + trackEvent.modelSelected(model); await api.executeClaudeCode(projectPath, prompt, model); } } @@ -821,6 +830,11 @@ export const ClaudeCodeSession: React.FC = ({ isMountedRef.current = false; isListeningRef.current = false; + // Track session completion + if (effectiveSession) { + trackEvent.sessionCompleted(); + } + // Clean up listeners unlistenRefs.current.forEach(unlisten => unlisten()); unlistenRefs.current = []; diff --git a/src/components/TabManager.tsx b/src/components/TabManager.tsx index cca89d3..d896a3c 100644 --- a/src/components/TabManager.tsx +++ b/src/components/TabManager.tsx @@ -4,6 +4,7 @@ import { X, Plus, MessageSquare, Bot, AlertCircle, Loader2, Folder, BarChart, Se import { useTabState } from '@/hooks/useTabState'; import { Tab } from '@/contexts/TabContext'; import { cn } from '@/lib/utils'; +import { useTrackEvent } from '@/hooks'; interface TabItemProps { tab: Tab; @@ -133,6 +134,9 @@ export const TabManager: React.FC = ({ className }) => { const scrollContainerRef = useRef(null); const [showLeftScroll, setShowLeftScroll] = useState(false); const [showRightScroll, setShowRightScroll] = useState(false); + + // Analytics tracking + const trackEvent = useTrackEvent(); // Listen for tab switch events useEffect(() => { @@ -151,10 +155,15 @@ export const TabManager: React.FC = ({ className }) => { useEffect(() => { const handleCreateTab = () => { createChatTab(); + trackEvent.tabCreated('chat'); }; const handleCloseTab = async () => { if (activeTabId) { + const tab = tabs.find(t => t.id === activeTabId); + if (tab) { + trackEvent.tabClosed(tab.type); + } await closeTab(activeTabId); } }; @@ -227,12 +236,17 @@ export const TabManager: React.FC = ({ className }) => { }; const handleCloseTab = async (id: string) => { + const tab = tabs.find(t => t.id === id); + if (tab) { + trackEvent.tabClosed(tab.type); + } await closeTab(id); }; const handleNewTab = () => { if (canAddTab()) { createProjectsTab(); + trackEvent.tabCreated('projects'); } };