From dadf8a5bc3372ef8520e9f170fd77250644e7cca Mon Sep 17 00:00:00 2001 From: elihahah666 Date: Thu, 28 May 2026 00:48:41 +0800 Subject: [PATCH] Add tracking for Automations, Plugin Detail & Loop (#3103) * Add tracking for Automations, Plugin Detail, and Plugin Loop - RoutinesSection: track new_automation, create, save, cancel, run_now, edit, pause, resume, delete, history button clicks - PluginDetailView: track back navigation and use_plugin action - PluginLoopHome: track clear_active, submit, card_details, card_use - Extend AutomationsClickProps with new CRUD elements - Add PluginDetailClickProps and PluginLoopClickProps contracts * fix: address review comments on plugin/automation tracking - Extract onBack handler in PluginDetailView to cover both error-path and success-path back buttons with tracking - Move create/save tracking from submit button onClick into the form submit handler to capture keyboard submissions and avoid false positives from validation failures * fix: move submit tracking into submit() handler in PluginLoopHome Same fix as RoutinesSection: tracking now fires inside submit() so keyboard Enter submissions are captured and the !trimmed guard prevents false positives. --------- Co-authored-by: qiongyu1999 <2694684348@qq.com> --- apps/web/src/analytics/events.ts | 16 ++++++++++++ apps/web/src/components/PluginDetailView.tsx | 13 ++++++++-- apps/web/src/components/PluginLoopHome.tsx | 10 +++++--- apps/web/src/components/RoutinesSection.tsx | 18 +++++++++++--- packages/contracts/src/analytics/events.ts | 26 +++++++++++++++++++- 5 files changed, 73 insertions(+), 10 deletions(-) diff --git a/apps/web/src/analytics/events.ts b/apps/web/src/analytics/events.ts index 91f8496b2d..525aff845c 100644 --- a/apps/web/src/analytics/events.ts +++ b/apps/web/src/analytics/events.ts @@ -36,6 +36,8 @@ import type { PluginsTemplatesDropdownClickProps, PluginsAvailableTabClickProps, PluginsSourcesTabClickProps, + PluginDetailClickProps, + PluginLoopClickProps, DesignSystemsTopClickProps, DesignSystemsTemplateCardClickProps, DesignSystemsTemplatesModalClickProps, @@ -326,6 +328,20 @@ export function trackPluginsSourcesTabClick( send(track, 'ui_click', props); } +export function trackPluginDetailClick( + track: Track, + props: PluginDetailClickProps, +): void { + send(track, 'ui_click', props); +} + +export function trackPluginLoopClick( + track: Track, + props: PluginLoopClickProps, +): void { + send(track, 'ui_click', props); +} + export function trackDesignSystemsTopClick( track: Track, props: DesignSystemsTopClickProps, diff --git a/apps/web/src/components/PluginDetailView.tsx b/apps/web/src/components/PluginDetailView.tsx index b9845346f8..33157063b3 100644 --- a/apps/web/src/components/PluginDetailView.tsx +++ b/apps/web/src/components/PluginDetailView.tsx @@ -12,6 +12,8 @@ import type { ApplyResult, InstalledPluginRecord } from '@open-design/contracts' import { applyPlugin } from '../state/projects'; import { navigate } from '../router'; import { useI18n } from '../i18n'; +import { useAnalytics } from '../analytics/provider'; +import { trackPluginDetailClick } from '../analytics/events'; interface Props { pluginId: string; @@ -19,11 +21,17 @@ interface Props { export function PluginDetailView(props: Props) { const { locale } = useI18n(); + const analytics = useAnalytics(); const [plugin, setPlugin] = useState(null); const [error, setError] = useState(null); const [applying, setApplying] = useState(false); const [applied, setApplied] = useState(null); + const onBack = () => { + trackPluginDetailClick(analytics.track, { page_name: 'plugins', area: 'plugin_detail', element: 'back', plugin_id: props.pluginId }); + navigate({ kind: 'marketplace' }); + }; + useEffect(() => { let cancelled = false; void fetch(`/api/plugins/${encodeURIComponent(props.pluginId)}`) @@ -47,7 +55,7 @@ export function PluginDetailView(props: Props) { if (error) { return (
-
Failed to load plugin: {error}
@@ -80,6 +88,7 @@ export function PluginDetailView(props: Props) { }>; const onUse = async () => { + trackPluginDetailClick(analytics.track, { page_name: 'plugins', area: 'plugin_detail', element: 'use_plugin', plugin_id: plugin.id }); setApplying(true); setError(null); const result = await applyPlugin(plugin.id, { locale }); @@ -100,7 +109,7 @@ export function PluginDetailView(props: Props) { diff --git a/apps/web/src/components/PluginLoopHome.tsx b/apps/web/src/components/PluginLoopHome.tsx index f6e76c3201..8f8b61732b 100644 --- a/apps/web/src/components/PluginLoopHome.tsx +++ b/apps/web/src/components/PluginLoopHome.tsx @@ -15,6 +15,8 @@ import { Icon } from './Icon'; import { PluginDetailsModal } from './PluginDetailsModal'; import { TrustBadge } from './TrustBadge'; import { authorInitials, derivePluginSourceLinks } from '../runtime/plugin-source'; +import { useAnalytics } from '../analytics/provider'; +import { trackPluginLoopClick } from '../analytics/events'; export interface PluginLoopSubmit { prompt: string; @@ -56,6 +58,7 @@ interface ActivePlugin { export function PluginLoopHome({ onSubmit }: Props) { const { locale } = useI18n(); + const analytics = useAnalytics(); const [plugins, setPlugins] = useState([]); const [loading, setLoading] = useState(true); const [pendingApplyId, setPendingApplyId] = useState(null); @@ -128,6 +131,7 @@ export function PluginLoopHome({ onSubmit }: Props) { function submit() { const trimmed = prompt.trim(); if (!trimmed) return; + trackPluginLoopClick(analytics.track, { page_name: 'plugins', area: 'plugin_loop', element: 'submit', plugin_id: active?.record.id }); onSubmit({ prompt: trimmed, pluginId: active?.record.id ?? null, @@ -168,7 +172,7 @@ export function PluginLoopHome({ onSubmit }: Props) {