From db1f2b4c4e0d44c36b030e465c7f987bc80ce761 Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Wed, 19 Mar 2025 10:10:45 +0100 Subject: [PATCH] feat: track new functions behaviour (#34239) --- .../Functions/FunctionsEmptyState.tsx | 46 ++++++- .../EdgeFunctionBlock/EdgeFunctionBlock.tsx | 10 ++ .../pages/project/[ref]/functions/index.tsx | 22 +++- .../pages/project/[ref]/functions/new.tsx | 19 +++ packages/common/telemetry-constants.ts | 114 ++++++++++++++++++ 5 files changed, 203 insertions(+), 8 deletions(-) diff --git a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx index 38044e82868..5ac058fbd3b 100644 --- a/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx +++ b/apps/studio/components/interfaces/Functions/FunctionsEmptyState.tsx @@ -2,6 +2,8 @@ import { useParams } from 'common' import { ScaffoldSectionTitle } from 'components/layouts/Scaffold' import { ResourceItem } from 'components/ui/Resource/ResourceItem' import { ResourceList } from 'components/ui/Resource/ResourceList' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useFlag } from 'hooks/ui/useFlag' import { Code, Terminal } from 'lucide-react' import Link from 'next/link' @@ -28,6 +30,9 @@ export const FunctionsEmptyState = () => { const { setAiAssistantPanel } = useAppStateSnapshot() const edgeFunctionCreate = useFlag('edgeFunctionCreate') + const { mutate: sendEvent } = useSendEventMutation() + const org = useSelectedOrganization() + return ( <> @@ -48,7 +53,17 @@ export const FunctionsEmptyState = () => { - + @@ -69,7 +84,7 @@ export const FunctionsEmptyState = () => {

@@ -99,7 +119,17 @@ export const FunctionsEmptyState = () => {

Create and edit functions directly in the browser. Download to local at any time.

- @@ -116,7 +146,13 @@ export const FunctionsEmptyState = () => { } - onClick={() => {}} + onClick={() => { + sendEvent({ + action: 'edge_function_template_clicked', + properties: { templateName: template.name, origin: 'functions_page' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) + }} >

{template.name}

diff --git a/apps/studio/components/ui/EdgeFunctionBlock/EdgeFunctionBlock.tsx b/apps/studio/components/ui/EdgeFunctionBlock/EdgeFunctionBlock.tsx index 39c9274ebca..6e6dd76d349 100644 --- a/apps/studio/components/ui/EdgeFunctionBlock/EdgeFunctionBlock.tsx +++ b/apps/studio/components/ui/EdgeFunctionBlock/EdgeFunctionBlock.tsx @@ -2,12 +2,14 @@ import { DragEvent, ReactNode, useState } from 'react' import { toast } from 'sonner' import { useParams } from 'common' import { useEdgeFunctionDeployMutation } from 'data/edge-functions/edge-functions-deploy-mutation' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { ReportBlockContainer } from 'components/interfaces/Reports/ReportBlock/ReportBlockContainer' import { Button, CodeBlock, cn, CodeBlockLang } from 'ui' import { Code } from 'lucide-react' import Link from 'next/link' import { useProjectSettingsV2Query } from 'data/config/project-settings-v2-query' import { useEdgeFunctionQuery } from 'data/edge-functions/edge-function-query' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { Admonition } from 'ui-patterns' interface EdgeFunctionBlockProps { @@ -43,6 +45,9 @@ export const EdgeFunctionBlock = ({ const { data: settings } = useProjectSettingsV2Query({ projectRef: ref }) const { data: existingFunction } = useEdgeFunctionQuery({ projectRef: ref, slug: functionName }) + const { mutate: sendEvent } = useSendEventMutation() + const org = useSelectedOrganization() + const { mutateAsync: deployFunction, isLoading: isDeploying } = useEdgeFunctionDeployMutation({ onSuccess: () => { setIsDeployed(true) @@ -67,6 +72,11 @@ export const EdgeFunctionBlock = ({ }, files: [{ name: 'index.ts', content: code }], }) + sendEvent({ + action: 'edge_function_deploy_button_clicked', + properties: { origin: 'functions_ai_assistant' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) } catch (error) { toast.error( `Failed to deploy function: ${error instanceof Error ? error.message : 'Unknown error'}` diff --git a/apps/studio/pages/project/[ref]/functions/index.tsx b/apps/studio/pages/project/[ref]/functions/index.tsx index 0b41e0a1cc8..fb51ac6c600 100644 --- a/apps/studio/pages/project/[ref]/functions/index.tsx +++ b/apps/studio/pages/project/[ref]/functions/index.tsx @@ -15,6 +15,8 @@ import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { DocsButton } from 'components/ui/DocsButton' import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useEdgeFunctionsQuery } from 'data/edge-functions/edge-functions-query' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useFlag } from 'hooks/ui/useFlag' import { useAppStateSnapshot } from 'state/app-state' import type { NextPageWithLayout } from 'types' @@ -43,6 +45,8 @@ const EdgeFunctionsPage: NextPageWithLayout = () => { isSuccess, } = useEdgeFunctionsQuery({ projectRef: ref }) const edgeFunctionCreate = useFlag('edgeFunctionCreate') + const { mutate: sendEvent } = useSendEventMutation() + const org = useSelectedOrganization() const hasFunctions = (functions ?? []).length > 0 @@ -74,7 +78,14 @@ const EdgeFunctionsPage: NextPageWithLayout = () => {
{edgeFunctionCreate && ( router.push(`/project/${ref}/functions/new`)} + onSelect={() => { + router.push(`/project/${ref}/functions/new`) + sendEvent({ + action: 'edge_function_via_editor_button_clicked', + properties: { origin: 'secondary_action' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) + }} className="gap-4" > @@ -106,7 +117,7 @@ const EdgeFunctionsPage: NextPageWithLayout = () => { type="default" className="px-1 pointer-events-auto" icon={} - onClick={() => + onClick={() => { setAiAssistantPanel({ open: true, initialInput: `Create a new edge function that ...`, @@ -120,7 +131,12 @@ const EdgeFunctionsPage: NextPageWithLayout = () => { ], }, }) - } + sendEvent({ + action: 'edge_function_ai_assistant_button_clicked', + properties: { origin: 'secondary_action' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) + }} tooltip={{ content: { side: 'bottom', diff --git a/apps/studio/pages/project/[ref]/functions/new.tsx b/apps/studio/pages/project/[ref]/functions/new.tsx index 9c1bd3d7c51..723bb8aa101 100644 --- a/apps/studio/pages/project/[ref]/functions/new.tsx +++ b/apps/studio/pages/project/[ref]/functions/new.tsx @@ -13,7 +13,9 @@ import EdgeFunctionsLayout from 'components/layouts/EdgeFunctionsLayout/EdgeFunc import { PageLayout } from 'components/layouts/PageLayout/PageLayout' import FileExplorerAndEditor from 'components/ui/FileExplorerAndEditor/FileExplorerAndEditor' import { useEdgeFunctionDeployMutation } from 'data/edge-functions/edge-functions-deploy-mutation' +import { useSendEventMutation } from 'data/telemetry/send-event-mutation' import { useOrgOptedIntoAi } from 'hooks/misc/useOrgOptedIntoAi' +import { useSelectedOrganization } from 'hooks/misc/useSelectedOrganization' import { useSelectedProject } from 'hooks/misc/useSelectedProject' import { useFlag } from 'hooks/ui/useFlag' import { BASE_PATH, IS_PLATFORM } from 'lib/constants' @@ -103,6 +105,8 @@ const NewFunctionPage = () => { const includeSchemaMetadata = isOptedInToAI || !IS_PLATFORM const { setAiAssistantPanel } = useAppStateSnapshot() const edgeFunctionCreate = useFlag('edgeFunctionCreate') + const { mutate: sendEvent } = useSendEventMutation() + const org = useSelectedOrganization() const [files, setFiles] = useState< { id: number; name: string; content: string; selected?: boolean }[] @@ -148,6 +152,11 @@ const NewFunctionPage = () => { }, files: files.map(({ name, content }) => ({ name, content })), }) + sendEvent({ + action: 'edge_function_deploy_button_clicked', + properties: { origin: 'functions_editor' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) } const handleChat = () => { @@ -167,6 +176,11 @@ const NewFunctionPage = () => { ], }, }) + sendEvent({ + action: 'edge_function_ai_assistant_button_clicked', + properties: { origin: 'functions_editor_chat' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) } const onSelectTemplate = (templateValue: string) => { @@ -176,6 +190,11 @@ const NewFunctionPage = () => { prev.map((file) => (file.selected ? { ...file, content: template.content } : file)) ) setOpen(false) + sendEvent({ + action: 'edge_function_template_clicked', + properties: { templateName: template.name, origin: 'editor_page' }, + groups: { project: ref ?? 'Unknown', organization: org?.slug ?? 'Unknown' }, + }) } } diff --git a/packages/common/telemetry-constants.ts b/packages/common/telemetry-constants.ts index 44fcc96628a..e90d847712c 100644 --- a/packages/common/telemetry-constants.ts +++ b/packages/common/telemetry-constants.ts @@ -1048,6 +1048,115 @@ export interface ReportsDatabaseGrafanaBannerClickedEvent { } } +/** + * User clicked the deploy button for an Edge Function. + * + * @group Events + * @source studio + * @page /dashboard/project/{ref}/functions/new + */ +export interface EdgeFunctionDeployButtonClickedEvent { + action: 'edge_function_deploy_button_clicked' + properties: { + /** + * Click on Deploy can either happen: + * 1. in the functions editor page + * 2. in the chat button in the functions editor + */ + origin: 'functions_editor' | 'functions_ai_assistant' + } + groups: { + project: string + organization: string + } +} + +/** + * User clicked the AI Assistant button to create an Edge Function. + * + * @group Events + * @source studio + * @page /dashboard/project/{ref}/functions + */ +export interface EdgeFunctionAiAssistantButtonClickedEvent { + action: 'edge_function_ai_assistant_button_clicked' + properties: { + /** + * Click on AI Assistant can either happen: + * 1. on the main block when there are no functions + * 2. in the secondary action section of the page + * 3. on the chat button in the functions editor + */ + origin: 'no_functions_block' | 'secondary_action' | 'functions_editor_chat' + } + groups: { + project: string + organization: string + } +} + +/** + * User clicked the button to go to the functions editor page to create an edge function. + * + * @group Events + * @source studio + * @page /dashboard/project/{ref}/functions + */ +export interface EdgeFunctionViaEditorButtonClickedEvent { + action: 'edge_function_via_editor_button_clicked' + properties: { + /** + * Click on Via Editor can either happen: + * 1. on the main block when there are no functions + * 2. in the secondary action section of the page + */ + origin: 'no_functions_block' | 'secondary_action' + } + groups: { + project: string + organization: string + } +} + +/** + * User clicked on an Edge Function template. + * + * @group Events + * @source studio + * @page /dashboard/project/{ref}/functions + */ +export interface EdgeFunctionTemplateClickedEvent { + action: 'edge_function_template_clicked' + properties: { + templateName: string + /** + * Where the edge function template was clicked from: + * 1. functions page + * 2. editor page + */ + origin: 'functions_page' | 'editor_page' + } + groups: { + project: string + organization: string + } +} + +/** + * User clicked the button to create an edge function via CLI. + * + * @group Events + * @source studio + * @page /dashboard/project/{ref}/functions + */ +export interface EdgeFunctionViaCliButtonClickedEvent { + action: 'edge_function_via_cli_button_clicked' + groups: { + project: string + organization: string + } +} + /** * @hidden */ @@ -1113,3 +1222,8 @@ export type TelemetryEvent = | StudioPricingPlanCtaClickedEvent | StudioPricingSidePanelOpenedEvent | ReportsDatabaseGrafanaBannerClickedEvent + | EdgeFunctionDeployButtonClickedEvent + | EdgeFunctionAiAssistantButtonClickedEvent + | EdgeFunctionViaEditorButtonClickedEvent + | EdgeFunctionTemplateClickedEvent + | EdgeFunctionViaCliButtonClickedEvent