Files
supabase/apps/studio/components/interfaces/ProjectHome/Home.tsx
T
Pamela ChiaandAlaister Young edacf2413d chore(studio): ship connect section, remove getting started and experiment plumbing (#44329)
## Summary

The `connectSection` A/B experiment concluded as a true null (no effect
on activation or any downstream metric after 13 days at 50/50, ~153K
mature orgs). Saxon decided to ship the Connect section as the permanent
experience. This PR removes the Getting Started control variant, the old
Connect modal, all experiment flag gating, and related telemetry types.

## Changes

- Delete `GettingStarted/` directory (5 files: section component, types,
utils, progress hook)
- Delete old `Connect.tsx` dialog modal (replaced by ConnectSheet)
- Remove `connectSection` PostHog flag reads from `Home.tsx` and
`LayoutHeader.tsx`
- Remove `getSectionVisibility()` experiment logic and
`ConnectSectionVariant` type
- Remove `getting-started` from `DEFAULT_SECTION_ORDER`
- Always render `<ConnectSheet />` in header (no more conditional with
old `<Connect />` modal)
- Remove `variant` prop from `ConnectSection` component
- Remove 4 getting-started telemetry event interfaces from
`telemetry-constants.ts`
- Update `mergeSectionOrder` tests to reflect new section order

## Testing

Tested on Vercel preview:
- [x] Project homepage shows Connect section for new projects (< 10 days
old)
- [x] Connect section hidden for mature projects (> 10 days old)
- [x] Header Connect button opens ConnectSheet (not old modal)
- [x] Connect tiles open ConnectSheet with correct tab
- [x] Section drag-and-drop still works without getting-started in the
order
- [x] Existing users with `getting-started` in localStorage order don't
break (mergeSectionOrder strips it)

## Linear

- fixes GROWTH-730

---------

Co-authored-by: Alaister Young <alaister@users.noreply.github.com>
2026-03-30 20:51:09 +08:00

141 lines
5.4 KiB
TypeScript

import { DndContext, DragEndEvent, PointerSensor, useSensor, useSensors } from '@dnd-kit/core'
import { arrayMove, SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
import { IS_PLATFORM, useFlag, useParams } from 'common'
import { ProjectUsageSection as ProjectUsageSectionV1 } from 'components/interfaces/Home/ProjectUsageSection'
import { SortableSection } from 'components/interfaces/ProjectHome/SortableSection'
import { TopSection } from 'components/interfaces/ProjectHome/TopSection'
import { ScaffoldContainer, ScaffoldSection } from 'components/layouts/Scaffold'
import dayjs from 'dayjs'
import { useLocalStorage } from 'hooks/misc/useLocalStorage'
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
import { PROJECT_STATUS } from 'lib/constants'
import { useTrack } from 'lib/telemetry/track'
import { useEffect, useRef } from 'react'
import { useAppStateSnapshot } from 'state/app-state'
import { cn } from 'ui'
import { AdvisorSection } from './AdvisorSection'
import { ConnectSection } from './ConnectSection'
import { CustomReportSection } from './CustomReportSection'
import { DEFAULT_SECTION_ORDER, mergeSectionOrder } from './Home.utils'
import { ProjectUsageSection as ProjectUsageSectionV2 } from './ProjectUsageSection'
export const ProjectHome = () => {
const { enableBranching } = useParams()
const snap = useAppStateSnapshot()
const { data: project } = useSelectedProjectQuery()
const track = useTrack()
const showHomepageUsageV2 = useFlag('newHomepageUsageV2')
const isMatureProject = dayjs(project?.inserted_at).isBefore(dayjs().subtract(10, 'day'))
const hasShownEnableBranchingModalRef = useRef(false)
const isPaused = project?.status === PROJECT_STATUS.INACTIVE
const isComingUp = project?.status === PROJECT_STATUS.COMING_UP
const [sectionOrder, setSectionOrder] = useLocalStorage<string[]>(
`home-section-order-${project?.ref || 'default'}`,
DEFAULT_SECTION_ORDER
)
const UsageSection = showHomepageUsageV2 ? ProjectUsageSectionV2 : ProjectUsageSectionV1
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }))
const handleDragEnd = (event: DragEndEvent) => {
const { active, over } = event
if (!over || active.id === over.id) return
setSectionOrder((items) => {
const oldIndex = items.indexOf(String(active.id))
const newIndex = items.indexOf(String(over.id))
if (oldIndex === -1 || newIndex === -1) return items
track('home_section_rows_moved', {
section_moved: String(active.id),
old_position: oldIndex,
new_position: newIndex,
})
return arrayMove(items, oldIndex, newIndex)
})
}
useEffect(() => {
if (enableBranching && !hasShownEnableBranchingModalRef.current) {
hasShownEnableBranchingModalRef.current = true
snap.setShowCreateBranchModal(true)
}
}, [enableBranching, snap])
useEffect(() => {
setSectionOrder(mergeSectionOrder)
}, [setSectionOrder])
const showConnectSection = !isMatureProject && !!project
const renderOrder = mergeSectionOrder(sectionOrder).filter((id) => {
if (id === 'connect') return showConnectSection
return true
})
return (
<div className="w-full h-full">
<ScaffoldContainer size="large" className={cn(isPaused && 'h-full')}>
<ScaffoldSection
isFullWidth
className={cn(isPaused ? 'h-full flex justify-center !p-0' : 'pb-0')}
>
<TopSection />
</ScaffoldSection>
</ScaffoldContainer>
{!isPaused && (
<ScaffoldContainer size="large">
<ScaffoldSection isFullWidth className="gap-12 pb-32">
<DndContext sensors={sensors} onDragEnd={handleDragEnd}>
<SortableContext items={renderOrder} strategy={verticalListSortingStrategy}>
{renderOrder.map((id) => {
if (IS_PLATFORM && id === 'usage') {
return (
<div key={id} className={cn(isComingUp && 'opacity-60 pointer-events-none')}>
<SortableSection id={id}>
<UsageSection />
</SortableSection>
</div>
)
}
if (id === 'connect' && showConnectSection) {
return (
<SortableSection key={id} id={id}>
<ConnectSection />
</SortableSection>
)
}
if (id === 'advisor') {
return (
<div key={id} className={cn(isComingUp && 'opacity-60 pointer-events-none')}>
<SortableSection id={id}>
<AdvisorSection showEmptyState={isComingUp} />
</SortableSection>
</div>
)
}
if (id === 'custom-report') {
return (
<div key={id} className={cn(isComingUp && 'opacity-60 pointer-events-none')}>
<SortableSection id={id}>
<CustomReportSection />
</SortableSection>
</div>
)
}
})}
</SortableContext>
</DndContext>
</ScaffoldSection>
</ScaffoldContainer>
)}
</div>
)
}