Skip to content

Commit ad6466b

Browse files
authored
feat: Add event tracking for table quickstart experiment (supabase#39854)
* Add event tracking for table quickstart experiment Implements telemetry instrumentation for the table quickstart experiment to measure exposure and engagement across AI, Templates, and Assistant variations. Events added: - table_quickstart_viewed: Exposure tracking for variant assignment - table_quickstart_ai_prompt_submitted: AI prompt submissions - table_quickstart_ai_generation_completed: AI generation success/failure - table_quickstart_template_selected: Template selection (unified for AI and templates) - table_quickstart_quick_idea_clicked: Quick idea button clicks - table_quickstart_category_selected: Template category navigation - table_quickstart_assistant_opened: Assistant chat creation All events use type-safe useTrack() hook with automatic project/org context. Conversion tracking intentionally omitted - relying on joins with existing table_created events for post-analysis. * event name cleanup * Address code review feedback - Use nullish coalescing operator (??) instead of logical OR (||) for categoryName fallback - Refactor handleGenerateTables to accept object parameter for improved call site readability * prettier * action key name alignment with event name
1 parent 26286fd commit ad6466b

File tree

4 files changed

+247
-13
lines changed

4 files changed

+247
-13
lines changed

apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/QuickstartAIWidget.tsx

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { AI_QUICK_IDEAS } from './constants'
1515
import type { TableSuggestion } from './types'
1616
import { useAITableGeneration } from './useAITableGeneration'
1717
import { convertTableSuggestionToTableField } from './utils'
18+
import { useTrack } from 'lib/telemetry/track'
1819

1920
interface QuickstartAIWidgetProps {
2021
onSelectTable: (tableData: Partial<TableField>) => void
@@ -26,6 +27,7 @@ const SUCCESS_MESSAGE_DURATION_MS = 3000
2627
export const QuickstartAIWidget = ({ onSelectTable, disabled }: QuickstartAIWidgetProps) => {
2728
const [lastGeneratedPrompt, setLastGeneratedPrompt] = useState('')
2829
const inputRef = useRef<HTMLInputElement>(null)
30+
const track = useTrack()
2931

3032
const {
3133
generateTables,
@@ -53,32 +55,66 @@ export const QuickstartAIWidget = ({ onSelectTable, disabled }: QuickstartAIWidg
5355

5456
const handleSelectTemplate = useCallback(
5557
(template: TableSuggestion) => {
58+
track('table_quickstart_template_clicked', {
59+
tableName: template.tableName,
60+
columnCount: template.fields.length,
61+
source: 'ai',
62+
})
63+
5664
const tableField = convertTableSuggestionToTableField(template)
5765
onSelectTable(tableField)
5866
toast.success(`Applied ${template.tableName} template. You can customize the fields below.`, {
5967
duration: SUCCESS_MESSAGE_DURATION_MS,
6068
})
6169
},
62-
[onSelectTable]
70+
[onSelectTable, track]
6371
)
6472

6573
const handleGenerateTables = useCallback(
66-
async (promptOverride?: string) => {
74+
async ({
75+
promptOverride,
76+
wasQuickIdea = false,
77+
}: { promptOverride?: string; wasQuickIdea?: boolean } = {}) => {
6778
const promptToUse = promptOverride ?? aiPrompt
6879
if (!promptToUse.trim() || isGenerating) return
6980

70-
await generateTables(promptToUse)
71-
setLastGeneratedPrompt(promptToUse)
81+
track('table_quickstart_ai_prompt_submitted', {
82+
promptLength: promptToUse.length,
83+
wasQuickIdea,
84+
})
85+
86+
try {
87+
const tables = await generateTables(promptToUse)
88+
89+
track('table_quickstart_ai_generation_completed', {
90+
success: tables.length > 0,
91+
tablesGenerated: tables.length,
92+
promptLength: promptToUse.length,
93+
})
94+
95+
setLastGeneratedPrompt(promptToUse)
96+
} catch (error) {
97+
track('table_quickstart_ai_generation_completed', {
98+
success: false,
99+
tablesGenerated: 0,
100+
promptLength: promptToUse.length,
101+
errorMessage: error instanceof Error ? error.message : 'Unknown error',
102+
})
103+
}
72104
},
73-
[aiPrompt, generateTables, isGenerating]
105+
[aiPrompt, generateTables, isGenerating, track]
74106
)
75107

76108
const handleQuickIdea = useCallback(
77109
(idea: string) => {
110+
track('table_quickstart_quick_idea_clicked', {
111+
ideaText: idea,
112+
})
113+
78114
setAiPrompt(idea)
79-
handleGenerateTables(idea)
115+
handleGenerateTables({ promptOverride: idea, wasQuickIdea: true })
80116
},
81-
[handleGenerateTables, setAiPrompt]
117+
[handleGenerateTables, setAiPrompt, track]
82118
)
83119

84120
return (

apps/studio/components/interfaces/TableGridEditor/SidePanelEditor/TableEditor/TableQuickstart/QuickstartTemplatesWidget.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { TableField } from '../TableEditor.types'
77
import { tableTemplates } from './templates'
88
import type { TableSuggestion } from './types'
99
import { convertTableSuggestionToTableField } from './utils'
10+
import { useTrack } from 'lib/telemetry/track'
1011

1112
interface QuickstartTemplatesWidgetProps {
1213
onSelectTemplate: (tableData: Partial<TableField>) => void
@@ -21,22 +22,40 @@ export const QuickstartTemplatesWidget = ({
2122
disabled,
2223
}: QuickstartTemplatesWidgetProps) => {
2324
const [activeCategory, setActiveCategory] = useState<string | null>(null)
25+
const track = useTrack()
2426

2527
useEffect(() => {
2628
if (activeCategory === null && CATEGORIES.length > 0) {
2729
setActiveCategory(CATEGORIES[0])
2830
}
2931
}, [activeCategory])
3032

33+
const handleCategorySelect = useCallback(
34+
(category: string) => {
35+
setActiveCategory(category)
36+
track('table_quickstart_category_clicked', {
37+
categoryName: category,
38+
})
39+
},
40+
[track]
41+
)
42+
3143
const handleSelectTemplate = useCallback(
3244
(template: TableSuggestion) => {
45+
track('table_quickstart_template_clicked', {
46+
tableName: template.tableName,
47+
columnCount: template.fields.length,
48+
source: 'templates',
49+
categoryName: activeCategory ?? 'Unknown',
50+
})
51+
3352
const tableField = convertTableSuggestionToTableField(template)
3453
onSelectTemplate(tableField)
3554
toast.success(`Applied ${template.tableName} template. You can customize the fields below.`, {
3655
duration: SUCCESS_MESSAGE_DURATION_MS,
3756
})
3857
},
39-
[onSelectTemplate]
58+
[onSelectTemplate, track, activeCategory]
4059
)
4160

4261
const displayedTemplates = activeCategory ? tableTemplates[activeCategory] || [] : []
@@ -58,7 +77,7 @@ export const QuickstartTemplatesWidget = ({
5877
{CATEGORIES.map((category) => (
5978
<button
6079
key={category}
61-
onClick={() => setActiveCategory(category)}
80+
onClick={() => handleCategorySelect(category)}
6281
disabled={disabled}
6382
role="tab"
6483
aria-selected={activeCategory === category}

apps/studio/components/layouts/Tabs/NewTab.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { partition } from 'lodash'
44
import { Table2 } from 'lucide-react'
55
import Link from 'next/link'
66
import { useRouter } from 'next/router'
7-
import { useMemo, useState } from 'react'
7+
import { useEffect, useMemo, useRef, useState } from 'react'
88
import { toast } from 'sonner'
99

1010
import { useParams } from 'common'
@@ -20,6 +20,7 @@ import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
2020
import { usePHFlag } from 'hooks/ui/useFlag'
2121
import { uuidv4 } from 'lib/helpers'
2222
import { useProfile } from 'lib/profile'
23+
import { useTrack } from 'lib/telemetry/track'
2324
import { AssistantMessageType, useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
2425
import { useSqlEditorV2StateSnapshot } from 'state/sql-editor-v2'
2526
import { useTableEditorStateSnapshot } from 'state/table-editor'
@@ -28,12 +29,12 @@ import { createTabId, useTabsStateSnapshot } from 'state/tabs'
2829
import {
2930
AiIconAnimation,
3031
Button,
31-
cn,
3232
SQL_ICON,
33-
Tabs_Shadcn_,
3433
TabsContent_Shadcn_,
3534
TabsList_Shadcn_,
3635
TabsTrigger_Shadcn_,
36+
Tabs_Shadcn_,
37+
cn,
3738
} from 'ui'
3839
import { useEditorType } from '../editors/EditorsLayout.hooks'
3940
import { ActionCard } from './ActionCard'
@@ -65,8 +66,10 @@ export function NewTab() {
6566
const [isCreatingChat, setIsCreatingChat] = useState(false)
6667
const [templates] = partition(SQL_TEMPLATES, { type: 'template' })
6768
const [quickstarts] = partition(SQL_TEMPLATES, { type: 'quickstart' })
69+
const hasTrackedExposure = useRef(false)
6870

6971
const { mutate: sendEvent } = useSendEventMutation()
72+
const track = useTrack()
7073
const { can: canCreateSQLSnippet } = useAsyncCheckPermissions(
7174
PermissionAction.CREATE,
7275
'user_content',
@@ -78,7 +81,7 @@ export function NewTab() {
7881

7982
/**
8083
* Returns:
81-
* - `QuickstartVariant`: user variation (if bucketed into AI, Templates, or future variants)
84+
* - `QuickstartVariant`: user variation (`ai`, `templates`, `assistant`)
8285
* - `false`: user not yet bucketed or not targeted for experiment
8386
* - `undefined`: PostHog still loading
8487
*/
@@ -99,6 +102,15 @@ export function NewTab() {
99102
? tableQuickstartVariant
100103
: null
101104

105+
useEffect(() => {
106+
if (activeQuickstartVariant && !hasTrackedExposure.current) {
107+
hasTrackedExposure.current = true
108+
track('table_quickstart_opened', {
109+
variant: activeQuickstartVariant,
110+
})
111+
}
112+
}, [activeQuickstartVariant, track])
113+
102114
const handleOpenAssistant = () => {
103115
if (isCreatingChat) return
104116

@@ -111,6 +123,9 @@ export function NewTab() {
111123
})
112124

113125
if (!chatId) {
126+
track('table_quickstart_assistant_opened', {
127+
chatCreated: false,
128+
})
114129
throw new Error('Failed to create chat')
115130
}
116131

@@ -127,6 +142,10 @@ export function NewTab() {
127142
}
128143

129144
aiSnap.saveMessage([userMessage, assistantMessage])
145+
146+
track('table_quickstart_assistant_opened', {
147+
chatCreated: true,
148+
})
130149
} catch (error) {
131150
console.error('Failed to open AI assistant:', error)
132151
const message = error instanceof Error ? error.message : 'Unknown error'

0 commit comments

Comments
 (0)