Files
supabase/apps/studio/components/layouts/editors/EditorBaseLayout.tsx
Charis 9aaa753306 feat(studio): add telemetry for explorer/sql editor temporary switch buttons (#49898)
## Summary

Add PostHog event tracking for the two new buttons introduced in PR
supabase/supabase#49698 that allow users to temporarily switch between
the Explorer and SQL Editor:

* **Explorer button**: "Back to SQL Editor" button in the Explorer
sidebar title bar now fires `explorer_temp_access_sql_editor_clicked`
event
* **SQL Editor button**: "Back to Explorer" button in the SQL Editor
title bar (shown during temporary visits) now fires
`sql_editor_back_explorer_clicked` event

Both event interfaces follow the repo's telemetry-standards conventions,
carrying only `groups: TelemetryGroups` property with no additional
custom properties.

## Test plan

- [X] Verify `explorer_temp_access_sql_editor_clicked` event fires in
PostHog when clicking "Back to SQL Editor" button in Explorer
- [X] Verify `sql_editor_back_explorer_clicked` event fires in PostHog
when clicking "Back to Explorer" button in SQL Editor
- [X] Run typecheck: `pnpm typecheck` passes without errors
- [X] Run lint: `pnpm lint --filter=studio` passes

## Issue

Resolves
[FE-4213](https://linear.app/supabase/issue/FE-4213/explorer-set-up-telemetry-for-metrics-where-appropriate)

## Summary by CodeRabbit

* **Analytics**
  * Added tracking for navigation from the Explorer to the SQL Editor.
  * Added tracking for returning from the SQL Editor to the Explorer.
2026-09-02 09:07:06 -04:00

106 lines
3.3 KiB
TypeScript

import { useParams } from 'common'
import { usePathname, useRouter } from 'next/navigation'
import { ComponentProps, ReactNode } from 'react'
import { cn } from 'ui'
import { EditorNavigationButton } from '../EditorNavigationButton'
import { ProjectLayoutWithAuth } from '../ProjectLayout'
import { CollapseButton } from '../Tabs/CollapseButton'
import { EditorTabs } from '../Tabs/Tabs'
import { useEditorType } from './EditorsLayout.hooks'
import { useIsTemporarySqlEditorVisit } from '@/hooks/misc/useIsTemporarySqlEditorVisit'
import { useTrack } from '@/lib/telemetry/track'
import { useTabsStateSnapshot } from '@/state/tabs'
export interface ExplorerLayoutProps extends ComponentProps<typeof ProjectLayoutWithAuth> {
children: ReactNode
title?: string
product?: string
productMenuClassName?: string
}
export const EditorBaseLayout = ({
children,
title,
product,
productMenuClassName,
productMenu,
browserTitle,
}: ExplorerLayoutProps) => {
const { ref } = useParams()
const pathname = usePathname()
const editor = useEditorType()
const tabs = useTabsStateSnapshot()
const hasNoOpenTabs =
editor === 'table' ? tabs.openTabs.filter((x) => !x.startsWith('sql')).length === 0 : false
const hideTabs =
pathname === `/project/${ref}/editor` || pathname === `/project/${ref}/sql` || hasNoOpenTabs
const activeEditorTab = tabs.activeTab ? tabs.tabsMap[tabs.activeTab] : undefined
// Prefer the live tab label so browser titles update immediately after a rename,
// even when persisted tab metadata is still catching up.
const activeEditorTabLabel = activeEditorTab?.label ?? activeEditorTab?.metadata?.name
const activeEditorTabEntity =
activeEditorTab === undefined
? undefined
: editor === 'sql'
? activeEditorTab.type === 'sql'
? activeEditorTabLabel
: undefined
: editor === 'table'
? activeEditorTab.type !== 'sql'
? activeEditorTabLabel
: undefined
: undefined
const mergedBrowserTitle = {
...browserTitle,
section: title ?? browserTitle?.section,
entity: browserTitle?.entity ?? activeEditorTabEntity,
}
return (
<ProjectLayoutWithAuth
resizableSidebar
product={product}
browserTitle={mergedBrowserTitle}
productMenuBadge={editor === 'sql' ? <BackToExplorerButton /> : undefined}
productMenuClassName={productMenuClassName}
productMenu={productMenu}
>
<div className="flex flex-col h-full">
<div
className={cn(
'h-10 md:min-h-(--header-height) flex items-center',
!hideTabs ? 'bg-surface-200 dark:bg-alternative' : 'bg-surface-100'
)}
>
{hideTabs ? <CollapseButton hideTabs={hideTabs} /> : <EditorTabs />}
</div>
<div className="h-full">{children}</div>
</div>
</ProjectLayoutWithAuth>
)
}
const BackToExplorerButton = () => {
const { ref } = useParams()
const router = useRouter()
const track = useTrack()
const { isTemporary, setIsTemporary } = useIsTemporarySqlEditorVisit(ref)
if (!ref || !isTemporary) return null
return (
<EditorNavigationButton
tooltip="Back to Explorer"
onClick={() => {
setIsTemporary(false)
track('sql_editor_back_explorer_clicked')
router.push(`/project/${ref}/explorer`)
}}
/>
)
}