Feat/add tracking and phflag for new home (#38853)

* add phflag

* track homev2 clicks

* track v2 drags

* remove old home tracking and extra visibility tracking

* add advisor, usage, and report clicks

* fix import type

* track remove charts from home

* rename actions to follow conventions

* fix lints

* simplify activity stats and add issue count property

* reduce useSelectedProjectQuery

* revert onClick and improve event properties

* Fix broken images

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
This commit is contained in:
Pamela Chia
2025-09-23 06:22:27 -07:00
committed by GitHub
parent c9033b1035
commit b3b5652555
10 changed files with 682 additions and 47 deletions

View File

@@ -62,6 +62,10 @@ export const ActivityStats = () => {
href={`/project/${ref}/database/migrations`}
icon={<Database size={18} strokeWidth={1.5} className="text-foreground" />}
label={<span>Last migration</span>}
trackingProperties={{
stat_type: 'migrations',
stat_value: migrationsData?.length ?? 0,
}}
value={
isLoadingMigrations ? (
<Skeleton className="h-6 w-24" />
@@ -81,6 +85,10 @@ export const ActivityStats = () => {
href={`/project/${ref}/database/backups/scheduled`}
icon={<Archive size={18} strokeWidth={1.5} className="text-foreground" />}
label={<span>Last backup</span>}
trackingProperties={{
stat_type: 'backups',
stat_value: backupsData?.backups?.length ?? 0,
}}
value={
isLoadingBackups ? (
<Skeleton className="h-6 w-24" />
@@ -103,6 +111,10 @@ export const ActivityStats = () => {
href={`/project/${ref}/branches`}
icon={<GitBranch size={18} strokeWidth={1.5} className="text-foreground" />}
label={<span>{isDefaultProject ? 'Recent branch' : 'Branch Created'}</span>}
trackingProperties={{
stat_type: 'branches',
stat_value: branchesData?.length ?? 0,
}}
value={
isLoadingBranches ? (
<Skeleton className="h-6 w-24" />

View File

@@ -10,6 +10,8 @@ import {
} from 'components/interfaces/Linter/Linter.utils'
import { ButtonTooltip } from 'components/ui/ButtonTooltip'
import { Lint, useProjectLintsQuery } from 'data/lint/lint-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import {
AiIconAnimation,
@@ -32,6 +34,8 @@ export const AdvisorSection = () => {
const { ref: projectRef } = useParams()
const { data: lints, isLoading: isLoadingLints } = useProjectLintsQuery({ projectRef })
const snap = useAiAssistantStateSnapshot()
const { mutate: sendEvent } = useSendEventMutation()
const { data: organization } = useSelectedOrganizationQuery()
const [selectedLint, setSelectedLint] = useState<Lint | null>(null)
@@ -54,11 +58,40 @@ export const AdvisorSection = () => {
const handleAskAssistant = useCallback(() => {
snap.toggleAssistant()
}, [snap])
if (projectRef && organization?.slug) {
sendEvent({
action: 'home_advisor_ask_assistant_clicked',
properties: {
issues_count: totalErrors,
},
groups: {
project: projectRef,
organization: organization.slug,
},
})
}
}, [snap, sendEvent, projectRef, organization, totalErrors])
const handleCardClick = useCallback((lint: Lint) => {
setSelectedLint(lint)
}, [])
const handleCardClick = useCallback(
(lint: Lint) => {
setSelectedLint(lint)
if (projectRef && organization?.slug) {
sendEvent({
action: 'home_advisor_issue_card_clicked',
properties: {
issue_category: lint.categories[0] || 'UNKNOWN',
issue_name: lint.name,
issues_count: totalErrors,
},
groups: {
project: projectRef,
organization: organization.slug,
},
})
}
},
[sendEvent, projectRef, organization]
)
return (
<div>
@@ -111,6 +144,19 @@ export const AdvisorSection = () => {
open: true,
initialInput: createLintSummaryPrompt(lint),
})
if (projectRef && organization?.slug) {
sendEvent({
action: 'home_advisor_fix_issue_clicked',
properties: {
issue_category: lint.categories[0] || 'UNKNOWN',
issue_name: lint.name,
},
groups: {
project: projectRef,
organization: organization.slug,
},
})
}
}}
tooltip={{
content: { side: 'bottom', text: 'Help me fix this issue' },

View File

@@ -23,7 +23,9 @@ import { AnalyticsInterval } from 'data/analytics/constants'
import { useContentInfiniteQuery } from 'data/content/content-infinite-query'
import { Content } from 'data/content/content-query'
import { useContentUpsertMutation } from 'data/content/content-upsert-mutation'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useAsyncCheckPermissions } from 'hooks/misc/useCheckPermissions'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { uuidv4 } from 'lib/helpers'
import { useProfile } from 'lib/profile'
import type { Dashboards } from 'types'
@@ -35,6 +37,8 @@ export function CustomReportSection() {
const endDate = dayjs().toISOString()
const { ref } = useParams()
const { profile } = useProfile()
const { mutate: sendEvent } = useSendEventMutation()
const { data: organization } = useSelectedOrganizationQuery()
const { data: reportsData } = useContentInfiniteQuery(
{ projectRef: ref, type: 'report', name: 'Home', limit: 1 },
@@ -185,6 +189,20 @@ export function CustomReportSection() {
content: newReport,
},
})
if (ref && organization?.slug) {
sendEvent({
action: 'home_custom_report_block_added',
properties: {
block_id: snippet.id,
position: 0,
},
groups: {
project: ref,
organization: organization.slug,
},
})
}
return
}
const current = [...editableReport.layout]
@@ -193,16 +211,46 @@ export function CustomReportSection() {
const updated = { ...editableReport, layout: current }
setEditableReport(updated)
persistReport(updated)
if (ref && organization?.slug) {
sendEvent({
action: 'home_custom_report_block_added',
properties: {
block_id: snippet.id,
position: current.length - 1,
},
groups: {
project: ref,
organization: organization.slug,
},
})
}
}
const handleRemoveChart = ({ metric }: { metric: { key: string } }) => {
if (!editableReport) return
const removedChart = editableReport.layout.find(
(x) => x.attribute === (metric.key as unknown as Dashboards.Chart['attribute'])
)
const nextLayout = editableReport.layout.filter(
(x) => x.attribute !== (metric.key as unknown as Dashboards.Chart['attribute'])
)
const updated = { ...editableReport, layout: nextLayout }
setEditableReport(updated)
persistReport(updated)
if (ref && organization?.slug && removedChart) {
sendEvent({
action: 'home_custom_report_block_removed',
properties: {
block_id: String(removedChart.id),
},
groups: {
project: ref,
organization: organization.slug,
},
})
}
}
const handleUpdateChart = (

View File

@@ -1,18 +1,45 @@
import { Check, ChevronLeft, ChevronRight } from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
import { useEffect, useState } from 'react'
import { Check, ChevronLeft, ChevronRight } from 'lucide-react'
import { cn, Button, Card, CardContent, CardHeader, CardTitle, Badge } from 'ui'
import { GettingStartedStep } from './GettingStartedSection'
import Image from 'next/image'
import { BASE_PATH } from 'lib/constants'
import { Badge, Button, Card, CardContent, cn } from 'ui'
import { GettingStartedAction, GettingStartedStep } from './GettingStartedSection'
// Determine action type for tracking
const getActionType = (action: GettingStartedAction): 'primary' | 'ai_assist' | 'external_link' => {
// Check if it's an AI assist action (has AiIconAnimation or "Do it for me"/"Generate" labels)
if (
action.label?.toLowerCase().includes('do it for me') ||
action.label?.toLowerCase().includes('generate') ||
action.label?.toLowerCase().includes('create policies for me')
) {
return 'ai_assist'
}
// Check if it's an external link (href that doesn't start with /project/)
if (action.href && !action.href.startsWith('/project/')) {
return 'external_link'
}
return 'primary'
}
export interface GettingStartedProps {
steps: GettingStartedStep[]
onStepClick: ({
stepIndex,
stepTitle,
actionType,
wasCompleted,
}: {
stepIndex: number
stepTitle: string
actionType: 'primary' | 'ai_assist' | 'external_link'
wasCompleted: boolean
}) => void
}
export function GettingStarted({ steps }: GettingStartedProps) {
export function GettingStarted({ steps, onStepClick }: GettingStartedProps) {
const [activeStepKey, setActiveStepKey] = useState<string | null>(steps[0]?.key ?? null)
useEffect(() => {
@@ -172,6 +199,8 @@ export function GettingStarted({ steps }: GettingStartedProps) {
return <div key={`${activeStep.key}-action-${i}`}>{action.component}</div>
}
const actionType = getActionType(action)
if (action.href) {
return (
<Button
@@ -181,7 +210,19 @@ export function GettingStarted({ steps }: GettingStartedProps) {
icon={action.icon}
className="text-foreground-light hover:text-foreground"
>
<Link href={action.href}>{action.label}</Link>
<Link
href={action.href}
onClick={() => {
onStepClick({
stepIndex: activeStepIndex,
stepTitle: activeStep.title,
actionType,
wasCompleted: activeStep.status === 'complete',
})
}}
>
{action.label}
</Link>
</Button>
)
}
@@ -191,7 +232,16 @@ export function GettingStarted({ steps }: GettingStartedProps) {
key={`${activeStep.key}-action-${i}`}
type={action.variant ?? 'default'}
icon={action.icon}
onClick={action.onClick}
onClick={() => {
action.onClick?.()
onStepClick({
stepIndex: activeStepIndex,
stepTitle: activeStep.title,
actionType,
wasCompleted: activeStep.status === 'complete',
})
}}
className="text-foreground-light hover:text-foreground"
>
{action.label}
</Button>

View File

@@ -1,26 +1,27 @@
import { useParams } from 'common'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { useRouter } from 'next/router'
import { useCallback, useMemo, useState } from 'react'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import { GettingStarted } from './GettingStarted'
import { FrameworkSelector } from './FrameworkSelector'
import {
BarChart3,
Code,
Database,
Table,
User,
Upload,
UserPlus,
BarChart3,
Shield,
Table2,
GitBranch,
Shield,
Table,
Table2,
Upload,
User,
UserPlus,
} from 'lucide-react'
import { useRouter } from 'next/router'
import { useCallback, useMemo, useState } from 'react'
import { useParams } from 'common'
import { FRAMEWORKS } from 'components/interfaces/Connect/Connect.constants'
import { useBranchesQuery } from 'data/branches/branches-query'
import { useTablesQuery } from 'data/tables/tables-query'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { BASE_PATH } from 'lib/constants'
import { useAiAssistantStateSnapshot } from 'state/ai-assistant-state'
import {
AiIconAnimation,
Button,
@@ -30,7 +31,8 @@ import {
ToggleGroup,
ToggleGroupItem,
} from 'ui'
import { BASE_PATH } from 'lib/constants'
import { FrameworkSelector } from './FrameworkSelector'
import { GettingStarted } from './GettingStarted'
export type GettingStartedAction = {
label: string
@@ -61,13 +63,16 @@ export function GettingStartedSection({
onChange: (v: GettingStartedState) => void
}) {
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const { ref } = useParams()
const aiSnap = useAiAssistantStateSnapshot()
const router = useRouter()
const { mutate: sendEvent } = useSendEventMutation()
// Local state for framework selector preview
const [selectedFramework, setSelectedFramework] = useState<string>(FRAMEWORKS[0]?.key ?? 'nextjs')
const workflow: 'no-code' | 'code' | null = value === 'code' || value === 'no-code' ? value : null
const [previousWorkflow, setPreviousWorkflow] = useState<'no-code' | 'code' | null>(null)
const { data: tablesData } = useTablesQuery({
projectRef: project?.ref,
@@ -156,7 +161,7 @@ export function GettingStartedSection({
status: tablesCount > 0 ? 'complete' : 'incomplete',
title: 'Design your database schema',
icon: <Database strokeWidth={1} className="text-foreground-muted" size={16} />,
image: '/img/getting-started/declarative-schemas.png',
image: `${BASE_PATH}/img/getting-started/declarative-schemas.png`,
description:
'Next, create a schema file that defines the structure of your database, either following our declarative schema guide or asking the AI assistant to generate one for you.',
actions: [
@@ -324,7 +329,7 @@ export function GettingStartedSection({
status: tablesCount > 0 ? 'complete' : 'incomplete',
title: 'Create your first table',
icon: <Database strokeWidth={1} className="text-foreground-muted" size={16} />,
image: '/img/getting-started/sample.png',
image: `${BASE_PATH}/img/getting-started/sample.png`,
description:
"To kick off your new project, let's start by creating your very first database table using either the table editor or the AI assistant to shape the structure for you.",
actions: [
@@ -488,7 +493,24 @@ export function GettingStartedSection({
<ToggleGroup
type="single"
value={workflow ?? undefined}
onValueChange={(v) => v && onChange(v as 'no-code' | 'code')}
onValueChange={(v) => {
if (v) {
const newWorkflow = v as 'no-code' | 'code'
setPreviousWorkflow(workflow)
onChange(newWorkflow)
sendEvent({
action: 'home_getting_started_workflow_clicked',
properties: {
workflow: newWorkflow === 'no-code' ? 'no_code' : 'code',
is_switch: previousWorkflow !== null,
},
groups: {
project: project?.ref || '',
organization: organization?.slug || '',
},
})
}
}}
>
<ToggleGroupItem
value="no-code"
@@ -509,7 +531,31 @@ export function GettingStartedSection({
No-code
</ToggleGroupItem>
</ToggleGroup>
<Button size="tiny" type="outline" onClick={() => onChange('hidden')}>
<Button
size="tiny"
type="outline"
onClick={() => {
onChange('hidden')
if (workflow) {
const completedSteps = (workflow === 'code' ? codeSteps : noCodeSteps).filter(
(step) => step.status === 'complete'
).length
const totalSteps = (workflow === 'code' ? codeSteps : noCodeSteps).length
sendEvent({
action: 'home_getting_started_closed',
properties: {
workflow: workflow === 'no-code' ? 'no_code' : 'code',
steps_completed: completedSteps,
total_steps: totalSteps,
},
groups: {
project: project?.ref || '',
organization: organization?.slug || '',
},
})
}
}}
>
Dismiss
</Button>
</div>
@@ -545,7 +591,21 @@ export function GettingStartedSection({
<Button
size="medium"
type="outline"
onClick={() => onChange('no-code')}
onClick={() => {
setPreviousWorkflow(workflow)
onChange('no-code')
sendEvent({
action: 'home_getting_started_workflow_clicked',
properties: {
workflow: 'no_code',
is_switch: previousWorkflow !== null,
},
groups: {
project: project?.ref || '',
organization: organization?.slug || '',
},
})
}}
className="block gap-2 h-auto p-4 md:p-8 max-w-80 text-left justify-start bg-background "
>
<Table2 size={20} strokeWidth={1.5} className="text-brand" />
@@ -559,7 +619,21 @@ export function GettingStartedSection({
<Button
size="medium"
type="outline"
onClick={() => onChange('code')}
onClick={() => {
setPreviousWorkflow(workflow)
onChange('code')
sendEvent({
action: 'home_getting_started_workflow_clicked',
properties: {
workflow: 'code',
is_switch: previousWorkflow !== null,
},
groups: {
project: project?.ref || '',
organization: organization?.slug || '',
},
})
}}
className="bg-background block gap-2 h-auto p-4 md:p-8 max-w-80 text-left justify-start"
>
<Code size={20} strokeWidth={1.5} className="text-brand" />
@@ -574,7 +648,27 @@ export function GettingStartedSection({
</CardContent>
</Card>
) : (
<GettingStarted steps={steps} />
<GettingStarted
steps={steps}
onStepClick={({ stepIndex, stepTitle, actionType, wasCompleted }) => {
if (workflow) {
sendEvent({
action: 'home_getting_started_step_clicked',
properties: {
workflow: workflow === 'no-code' ? 'no_code' : 'code',
step_number: stepIndex + 1,
step_title: stepTitle,
action_type: actionType,
was_completed: wasCompleted,
},
groups: {
project: project?.ref || '',
organization: organization?.slug || '',
},
})
}
}}
/>
)}
</section>
)

View File

@@ -3,6 +3,7 @@ import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-ki
import { useEffect, useRef } from 'react'
import { IS_PLATFORM, useParams } from 'common'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { SortableSection } from 'components/interfaces/HomeNew/SortableSection'
import { TopSection } from 'components/interfaces/HomeNew/TopSection'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
@@ -31,6 +32,7 @@ export const HomeV2 = () => {
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const { data: parentProject } = useProjectByRefQuery(project?.parent_project_ref)
const { mutate: sendEvent } = useSendEventMutation()
const hasShownEnableBranchingModalRef = useRef(false)
const isPaused = project?.status === PROJECT_STATUS.INACTIVE
@@ -69,6 +71,22 @@ export const HomeV2 = () => {
const oldIndex = items.indexOf(String(active.id))
const newIndex = items.indexOf(String(over.id))
if (oldIndex === -1 || newIndex === -1) return items
if (project?.ref && organization?.slug) {
sendEvent({
action: 'home_section_rows_moved',
properties: {
section_moved: String(active.id),
old_position: oldIndex,
new_position: newIndex,
},
groups: {
project: project.ref,
organization: organization.slug,
},
})
}
return arrayMove(items, oldIndex, newIndex)
})
}

View File

@@ -1,5 +1,5 @@
import dayjs from 'dayjs'
import { Archive, Code, Database, Key, Zap, ChevronDown } from 'lucide-react'
import { Archive, ChevronDown, Code, Database, Key, Zap } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useMemo, useState } from 'react'
@@ -7,6 +7,7 @@ import { useMemo, useState } from 'react'
import { useParams } from 'common'
import NoDataPlaceholder from 'components/ui/Charts/NoDataPlaceholder'
import { InlineLink } from 'components/ui/InlineLink'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import useProjectUsageStats from 'hooks/analytics/useProjectUsageStats'
import { useCurrentOrgPlan } from 'hooks/misc/useCurrentOrgPlan'
import { useIsFeatureEnabled } from 'hooks/misc/useIsFeatureEnabled'
@@ -19,14 +20,14 @@ import {
CardHeader,
CardTitle,
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
TooltipTrigger,
DropdownMenuTrigger,
Loading,
Tooltip,
TooltipContent,
TooltipTrigger,
cn,
} from 'ui'
import { Row } from 'ui-patterns'
@@ -94,6 +95,7 @@ export const ProjectUsageSection = () => {
const router = useRouter()
const { ref: projectRef } = useParams()
const { data: organization } = useSelectedOrganizationQuery()
const { mutate: sendEvent } = useSendEventMutation()
const { projectAuthAll: authEnabled, projectStorageAll: storageEnabled } = useIsFeatureEnabled([
'project_auth:all',
'project_storage:all',
@@ -211,7 +213,7 @@ export const ProjectUsageSection = () => {
const isLoading = services.some((s) => s.stats.isLoading)
const handleBarClick = (logRoute: string) => (datum: any) => {
const handleBarClick = (logRoute: string, serviceKey: ServiceKey) => (datum: any) => {
if (!datum?.timestamp) return
const datumTimestamp = dayjs(datum.timestamp).toISOString()
@@ -224,6 +226,20 @@ export const ProjectUsageSection = () => {
})
router.push(`/project/${projectRef}${logRoute}?${queryParams.toString()}`)
if (projectRef && organization?.slug) {
sendEvent({
action: 'home_project_usage_chart_clicked',
properties: {
service_type: serviceKey,
bar_timestamp: datum.timestamp,
},
groups: {
project: projectRef,
organization: organization.slug,
},
})
}
}
const enabledServices = services.filter((s) => s.enabled)
@@ -350,7 +366,31 @@ export const ProjectUsageSection = () => {
<div className="flex flex-col">
<div className="flex items-center gap-2">
<CardTitle className="text-foreground-light">
{s.href ? <Link href={s.href}>{s.title}</Link> : s.title}
{s.href ? (
<Link
href={s.href}
onClick={() => {
if (projectRef && organization?.slug) {
sendEvent({
action: 'home_project_usage_service_clicked',
properties: {
service_type: s.key,
total_requests: s.total || 0,
error_count: s.err || 0,
},
groups: {
project: projectRef,
organization: organization.slug,
},
})
}
}}
>
{s.title}
</Link>
) : (
s.title
)}
</CardTitle>
</div>
<span className="text-foreground text-xl">{(s.total || 0).toLocaleString()}</span>
@@ -376,10 +416,10 @@ export const ProjectUsageSection = () => {
<CardContent className="p-6 pt-4 flex-1 h-full overflow-hidden">
<Loading isFullHeight active={isLoading}>
<LogsBarChart
isFullHeight
data={s.data}
DateTimeFormat={datetimeFormat}
onBarClick={handleBarClick(s.route)}
isFullHeight
onBarClick={handleBarClick(s.route, s.key)}
EmptyState={
<NoDataPlaceholder
size="small"

View File

@@ -1,6 +1,10 @@
import Link from 'next/link'
import type { ReactNode } from 'react'
import { useSendEventMutation } from 'data/telemetry/send-event-mutation'
import { useSelectedOrganizationQuery } from 'hooks/misc/useSelectedOrganization'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
type SingleStatProps = {
icon: ReactNode
label: ReactNode
@@ -8,9 +12,37 @@ type SingleStatProps = {
className?: string
href?: string
onClick?: () => void
trackingProperties?: {
stat_type: 'migrations' | 'backups' | 'branches'
stat_value: number
}
}
export const SingleStat = ({ icon, label, value, className, href, onClick }: SingleStatProps) => {
export const SingleStat = ({
icon,
label,
value,
className,
href,
onClick,
trackingProperties,
}: SingleStatProps) => {
const { mutate: sendEvent } = useSendEventMutation()
const { data: project } = useSelectedProjectQuery()
const { data: organization } = useSelectedOrganizationQuery()
const trackActivityStat = () => {
if (trackingProperties && project?.ref && organization?.slug) {
sendEvent({
action: 'home_activity_stat_clicked',
properties: trackingProperties,
groups: {
project: project.ref,
organization: organization.slug,
},
})
}
}
const content = (
<div className={`group flex items-center gap-4 p-0 text-base justify-start ${className || ''}`}>
<div className="w-16 h-16 rounded-md bg-surface-75 group-hover:bg-muted border flex items-center justify-center">
@@ -27,7 +59,7 @@ export const SingleStat = ({ icon, label, value, className, href, onClick }: Sin
if (href) {
return (
<Link className="group block" href={href}>
<Link className="group block" href={href} onClick={trackActivityStat}>
{content}
</Link>
)

View File

@@ -3,12 +3,14 @@ import { Home } from 'components/interfaces/Home/Home'
import { HomeV2 } from 'components/interfaces/HomeNew/Home'
import DefaultLayout from 'components/layouts/DefaultLayout'
import { ProjectLayoutWithAuth } from 'components/layouts/ProjectLayout/ProjectLayout'
import { usePHFlag } from 'hooks/ui/useFlag'
import type { NextPageWithLayout } from 'types'
const HomePage: NextPageWithLayout = () => {
const isHomeNew = useFlag('homeNew')
const isHomeNewPH = usePHFlag('homeNew')
if (isHomeNew) {
if (isHomeNew && isHomeNewPH) {
return <HomeV2 />
}
return <Home />

View File

@@ -1422,6 +1422,287 @@ export interface DpaPdfOpenedEvent {
}
}
/**
* User selected a workflow in the Getting Started section of HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeGettingStartedWorkflowClickedEvent {
action: 'home_getting_started_workflow_clicked'
properties: {
/**
* The workflow selected by the user
*/
workflow: 'code' | 'no_code'
/**
* Whether this is switching from another workflow
*/
is_switch: boolean
}
groups: TelemetryGroups
}
/**
* User clicked on a step in the Getting Started section of HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeGettingStartedStepClickedEvent {
action: 'home_getting_started_step_clicked'
properties: {
/**
* The workflow type (code or no-code)
*/
workflow: 'code' | 'no_code'
/**
* The step number (1-based index)
*/
step_number: number
/**
* The title of the step
*/
step_title: string
/**
* The action type of the button clicked
*/
action_type: 'primary' | 'ai_assist' | 'external_link'
/**
* Whether the step was already completed
*/
was_completed: boolean
}
groups: TelemetryGroups
}
/**
* User clicked on an activity stat in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeActivityStatClickedEvent {
action: 'home_activity_stat_clicked'
properties: {
/**
* The type of activity stat clicked
*/
stat_type: 'migrations' | 'backups' | 'branches'
/**
* The current value of the stat
*/
stat_value: number
}
groups: TelemetryGroups
}
/**
* User clicked the main Ask Assistant button in the Advisor section of HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeAdvisorAskAssistantClickedEvent {
action: 'home_advisor_ask_assistant_clicked'
properties: {
/**
* Number of issues found by the advisor
*/
issues_count: number
}
groups: TelemetryGroups
}
/**
* User clicked on an issue card in the Advisor section of HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeAdvisorIssueCardClickedEvent {
action: 'home_advisor_issue_card_clicked'
properties: {
/**
* Category of the issue (SECURITY or PERFORMANCE)
*/
issue_category: string
/**
* Name/key of the lint issue
*/
issue_name: string
issues_count: number
}
groups: TelemetryGroups
}
/**
* User clicked the Fix Issue button on an advisor card in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeAdvisorFixIssueClickedEvent {
action: 'home_advisor_fix_issue_clicked'
properties: {
/**
* Category of the issue (SECURITY or PERFORMANCE)
*/
issue_category: string
/**
* Name/key of the lint issue
*/
issue_name: string
}
groups: TelemetryGroups
}
/**
* User clicked on a service title in Project Usage section of HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeProjectUsageServiceClickedEvent {
action: 'home_project_usage_service_clicked'
properties: {
/**
* The service that was clicked
*/
service_type: 'db' | 'functions' | 'auth' | 'storage' | 'realtime'
/**
* Total requests for this service
*/
total_requests: number
/**
* Number of errors for this service
*/
error_count: number
}
groups: TelemetryGroups
}
/**
* User clicked on a bar in the usage chart in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeProjectUsageChartClickedEvent {
action: 'home_project_usage_chart_clicked'
properties: {
/**
* The service type for this chart
*/
service_type: 'db' | 'functions' | 'auth' | 'storage' | 'realtime'
/**
* Timestamp of the bar clicked
*/
bar_timestamp: string
}
groups: TelemetryGroups
}
/**
* User added a block to the custom report in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeCustomReportBlockAddedEvent {
action: 'home_custom_report_block_added'
properties: {
/**
* ID of the snippet/block added
*/
block_id: string
/**
* If position is 0 it is equivalent to 'Add your first chart'.
*/
position: number
}
groups: TelemetryGroups
}
/**
* User removed a block from the custom report in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeCustomReportBlockRemovedEvent {
action: 'home_custom_report_block_removed'
properties: {
/**
* ID of the block removed
*/
block_id: string
}
groups: TelemetryGroups
}
/**
* User dismissed the Getting Started section in HomeV2.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeGettingStartedClosedEvent {
action: 'home_getting_started_closed'
properties: {
/**
* The current workflow when dismissed
*/
workflow: 'code' | 'no_code'
/**
* Number of steps completed when dismissed
*/
steps_completed: number
/**
* Total number of steps in the workflow
*/
total_steps: number
}
groups: TelemetryGroups
}
/**
* User reordered sections in HomeV2 using drag and drop.
*
* @group Events
* @source studio
* @page /project/{ref}
*/
export interface HomeSectionRowsMovedEvent {
action: 'home_section_rows_moved'
properties: {
/**
* The section that was moved
*/
section_moved: string
/**
* The old position of the section (0-based index)
*/
old_position: number
/**
* The new position of the section (0-based index)
*/
new_position: number
}
groups: TelemetryGroups
}
/**
* User clicked the Request DPA button to open the confirmation modal.
*
@@ -1555,6 +1836,18 @@ export type TelemetryEvent =
| BranchUpdatedEvent
| BranchReviewWithAssistantClickedEvent
| DpaPdfOpenedEvent
| HomeGettingStartedWorkflowClickedEvent
| HomeGettingStartedStepClickedEvent
| HomeGettingStartedClosedEvent
| HomeSectionRowsMovedEvent
| HomeActivityStatClickedEvent
| HomeAdvisorAskAssistantClickedEvent
| HomeAdvisorIssueCardClickedEvent
| HomeAdvisorFixIssueClickedEvent
| HomeProjectUsageServiceClickedEvent
| HomeProjectUsageChartClickedEvent
| HomeCustomReportBlockAddedEvent
| HomeCustomReportBlockRemovedEvent
| DpaRequestButtonClickedEvent
| DocumentViewButtonClickedEvent
| HipaaRequestButtonClickedEvent