mirror of
https://github.com/supabase/supabase.git
synced 2026-10-06 18:05:11 +03:00
## Summary Re-ports PR #43119 against the current `ProjectHome` codebase (the original PR targeted `HomeNew/` which was removed during the `homeNew` graduation in #43437). Also unifies the `connectSheet` and `connectSection` feature flags into a single `connectSection` flag — the `connectSheet` flag is removed entirely, so both the ConnectSheet (header) and ConnectSection (homepage row) are controlled by one experiment. - Adds `connectSection` PostHog experiment flag that controls two things: 1. Swaps the Getting Started section for a Connect section on the project homepage for new projects (< 10 days old) 2. Swaps the legacy Connect dialog for the new ConnectSheet panel in the header - **Control** (`getting-started`): existing Getting Started section + legacy Connect dialog in header - **Treatment** (`connect`): new 4-tile Connect section + ConnectSheet in header - `undefined` (loading): neither section renders, avoiding flash - Tiles filtered by the same `useIsFeatureEnabled` flags as ConnectSheet (`show_app_frameworks`, `show_mobile_frameworks`, `show_orms`) - Connect tile clicks tracked via `home_connect_action_clicked`; section render tracked via `home_connect_section_exposed`; sheet opens tracked via `connect_sheet_opened` with source attribution (`header_button` or `connect_section`) ## Changes - `packages/common/telemetry-constants.ts` — new `home_connect_section_exposed`, `home_connect_action_clicked`, and `connect_sheet_opened` event types - `ConnectSheet/ConnectSheet.tsx` — read `connectTab` query param and sync to active mode on open; `handleModeChange` keeps param in sync on tab switch; fire `connect_sheet_opened` event with source attribution on open - `ConnectButton/ConnectButton.tsx` — set `connectSource=header_button` query param on click - `ProjectHome/ConnectSection.tsx` — new component (4-tile connect card); set `connectSource=connect_section` on tile click - `ProjectHome/Home.tsx` — experiment flag wiring - `LayoutHeader/LayoutHeader.tsx` — read `connectSection` flag instead of `connectSheet` to toggle ConnectSheet vs legacy Connect dialog ## Test plan To test on the Vercel preview, set `connectSection=connect` to 100% in PostHog (or override via cookie `ph_override_connectSection=connect`). No separate `connectSheet` flag is needed — `connectSection` controls both features. - [x] Treatment (`connect` variant) — "Get connected" section renders on new project, Getting Started hidden - [x] Control (`getting-started`) — Getting Started renders, Connect section hidden - [x] Mature project (> 10 days) — neither section regardless of flag - [x] Clicking each tile opens ConnectSheet on the correct tab (Framework / Direct / ORM / MCP) - [x] Switching tabs inside sheet updates `connectTab` URL param - [x] Closing sheet clears `connectTab` param - [x] Direct URL deep-link (`?showConnect=true&connectTab=orm`) opens sheet on correct tab - [x] Dark mode — background gradient renders correctly - [x] Light mode — background switches to light gradient - [x] Responsive layout — 4 cols (xl), 2×2 (md), stacked (mobile) - [x] Telemetry — `home_connect_section_exposed` fires once on load; `home_connect_action_clicked` fires with correct `mode` - [x] Treatment — header Connect button opens ConnectSheet (not legacy Connect dialog) - [x] Control — header Connect button opens legacy Connect dialog (not ConnectSheet) - [x] Telemetry — clicking a ConnectSection tile fires `connect_sheet_opened` with `source: 'connect_section'` - [x] Telemetry — clicking header Connect button fires `connect_sheet_opened` with `source: 'header_button'` --------- Co-authored-by: Ali Waseem <waseema393@gmail.com>
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
import { IS_PLATFORM } from 'common'
|
|
import { useSelectedProjectQuery } from 'hooks/misc/useSelectedProject'
|
|
import { BASE_PATH, PROJECT_STATUS } from 'lib/constants'
|
|
import { useTrack } from 'lib/telemetry/track'
|
|
import { parseAsBoolean, parseAsString, useQueryState } from 'nuqs'
|
|
import { useEffect, useRef } from 'react'
|
|
import { Card, CardContent, cn } from 'ui'
|
|
|
|
import type { ConnectMode } from '../ConnectSheet/Connect.types'
|
|
import { useAvailableConnectModes } from '../ConnectSheet/useAvailableConnectModes'
|
|
import { useAppStateSnapshot } from '@/state/app-state'
|
|
import { CONNECT_ACTIONS, type ConnectSectionVariant } from './ConnectSection.config'
|
|
|
|
interface ConnectSectionProps {
|
|
variant: ConnectSectionVariant
|
|
}
|
|
|
|
export const ConnectSection = ({ variant }: ConnectSectionProps) => {
|
|
const { data: selectedProject } = useSelectedProjectQuery()
|
|
const { setConnectSheetSource } = useAppStateSnapshot()
|
|
const track = useTrack()
|
|
const [, setShowConnect] = useQueryState('showConnect', parseAsBoolean.withDefault(false))
|
|
const [, setConnectTab] = useQueryState('connectTab', parseAsString)
|
|
|
|
const isActiveHealthy = selectedProject?.status === PROJECT_STATUS.ACTIVE_HEALTHY
|
|
|
|
const availableModeIds = useAvailableConnectModes()
|
|
const availableActions = CONNECT_ACTIONS.filter((action) =>
|
|
availableModeIds.includes(action.mode)
|
|
)
|
|
|
|
const hasTrackedExposure = useRef(false)
|
|
|
|
useEffect(() => {
|
|
if (!IS_PLATFORM) return
|
|
if (hasTrackedExposure.current) return
|
|
hasTrackedExposure.current = true
|
|
track('home_connect_section_exposed', { variant })
|
|
}, [variant, track])
|
|
|
|
const handleConnectClick = (mode: ConnectMode) => {
|
|
track('home_connect_action_clicked', { mode })
|
|
setConnectTab(mode)
|
|
setConnectSheetSource('connect_section')
|
|
setShowConnect(true)
|
|
}
|
|
|
|
return (
|
|
<section className="w-full">
|
|
<div className="mb-6">
|
|
<h3 className="heading-section">Get connected</h3>
|
|
</div>
|
|
|
|
<Card className="bg-background/25 border-dashed relative overflow-hidden">
|
|
<div className="absolute -inset-16 z-0 opacity-50">
|
|
<img
|
|
src={`${BASE_PATH}/img/reports/bg-grafana-dark.svg`}
|
|
alt=""
|
|
className="w-full h-full object-cover object-right hidden dark:block"
|
|
/>
|
|
<img
|
|
src={`${BASE_PATH}/img/reports/bg-grafana-light.svg`}
|
|
alt=""
|
|
className="w-full h-full object-cover object-right dark:hidden"
|
|
/>
|
|
<div className="absolute inset-0 bg-gradient-to-r from-background-alternative to-transparent" />
|
|
</div>
|
|
|
|
<CardContent className="relative z-10 p-0">
|
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 divide-y md:divide-y-0 md:divide-x border-muted">
|
|
{availableActions.map((action) => (
|
|
<button
|
|
key={action.mode}
|
|
type="button"
|
|
disabled={!isActiveHealthy}
|
|
onClick={() => handleConnectClick(action.mode)}
|
|
className={cn(
|
|
'group flex flex-col items-center justify-center gap-3 p-6 text-center transition-colors min-h-32',
|
|
'hover:bg-surface-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand',
|
|
!isActiveHealthy && 'cursor-not-allowed opacity-50'
|
|
)}
|
|
>
|
|
<span className="text-foreground-light group-hover:text-foreground">
|
|
{action.icon}
|
|
</span>
|
|
<div className="flex flex-col gap-1">
|
|
<p className="text-sm">{action.heading}</p>
|
|
<p className="text-sm text-foreground-lighter">{action.subheading}</p>
|
|
</div>
|
|
</button>
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</section>
|
|
)
|
|
}
|