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:
@@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -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 = [];
|
||||||
|
@@ -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;
|
||||||
@@ -134,6 +135,9 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
|
|||||||
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(() => {
|
||||||
const handleSwitchToTab = (event: CustomEvent) => {
|
const handleSwitchToTab = (event: CustomEvent) => {
|
||||||
@@ -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');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user