diff --git a/src/components/ReaderView/index.tsx b/src/components/ReaderView/index.tsx index e966a5bb6f32..d9eccfda29c9 100644 --- a/src/components/ReaderView/index.tsx +++ b/src/components/ReaderView/index.tsx @@ -44,6 +44,7 @@ import { Questions } from 'components/Squeak' import { DocsPageSurvey } from 'components/DocsPageSurvey' import CopyMarkdownActionsDropdown, { useMarkdownUrlExists } from 'components/MarkdownActionsDropdown' import CustomerMetadata from './CustomerMetadata' +import WarehouseWizardHint from 'components/WarehouseWizardHint' import { getVideoClasses } from '../../constants' import AboutPostHog from 'components/AboutPostHog' @@ -1375,7 +1376,7 @@ function ReaderViewContent({ }: ReaderViewProps) { const { compact } = useApp() const { appWindow, activeInternalMenu } = useWindow() - const { hash } = useLocation() + const { hash, pathname } = useLocation() const contentRef = useRef(null) const articleColumnRef = useRef(null) @@ -1594,6 +1595,25 @@ function ReaderViewContent({ {title} )} + {/* Nudge to the setup wizard on the data-warehouse sources docs (root + + every child page). Templates that hide the ReaderView title and render + their own heading (e.g. DataWarehouseSource) add the hint themselves, so + the `!hideTitle` guard keeps this from double-rendering there. */} + {!hideTitle && + (pathname === '/docs/data-warehouse/sources' || + pathname?.startsWith('/docs/data-warehouse/sources/')) && ( +
+ +
+ )} {(body?.date || body?.contributors || body?.tags) && (
+``` + +### Props + +| Prop | Type | Default | Description | +| ----------- | -------- | ------- | ---------------------------------------------- | +| `className` | `string` | `''` | Extra classes applied to the outer card `div`. | + +## Where it renders + +- `/data-stack/sources` — added directly in `src/pages/data-stack/sources.tsx`. +- `/docs/data-warehouse/sources` and every page under that URL — for pages whose heading is + rendered by `ReaderView` (the docs root and MDX source docs), it is injected once in + `src/components/ReaderView/index.tsx`, gated on the pathname. For the pure-React warehouse + source template it is added directly in `src/templates/DataWarehouseSource.tsx` (that template + hides the `ReaderView` title and renders its own heading, so the shared injection is skipped + for it via the `!hideTitle` guard). + +## Notes + +- SSR-safe: the card starts hidden and only appears after a client-side `useEffect` reads + `localStorage`, so build/SSR renders nothing and previously-dismissed users see no flash. diff --git a/src/components/WarehouseWizardHint/index.tsx b/src/components/WarehouseWizardHint/index.tsx new file mode 100644 index 000000000000..3f6d5a3b4901 --- /dev/null +++ b/src/components/WarehouseWizardHint/index.tsx @@ -0,0 +1,59 @@ +import React, { useEffect, useState } from 'react' +import { IconSparkles, IconX } from '@posthog/icons' +import WizardCommand from 'components/WizardCommand' + +// Persist dismissal so the hint doesn't nag a user who has already seen it. Mirrors the +// product app's WarehouseWizardHint, which uses the same localStorage key. +const DISMISSED_KEY = 'warehouse-wizard-hint-dismissed' + +/** + * Agent-flavored nudge that pushes the `npx @posthog/wizard warehouse` CLI, which auto-detects + * and connects a user's databases/APIs straight from their codebase instead of setting up a + * source by hand. Ported from the PostHog product app so the same prompt shows on the marketing + * site's data-source pages. + */ +export default function WarehouseWizardHint({ className = '' }: { className?: string }): JSX.Element | null { + // Start hidden so SSR renders nothing and a user who already dismissed it never sees a flash. + // The effect reveals the hint on the client unless it was previously dismissed. + const [hidden, setHidden] = useState(true) + + useEffect(() => { + setHidden(localStorage.getItem(DISMISSED_KEY) === '1') + }, []) + + if (hidden) { + return null + } + + const handleDismiss = () => { + localStorage.setItem(DISMISSED_KEY, '1') + setHidden(true) + } + + return ( +
+ +
+ +

Let AI connect your sources for you

+
+

+ Skip the manual setup — run this in your project and the wizard auto-detects your databases and APIs and + connects them to PostHog. +

+
+ +
+
+ ) +} diff --git a/src/pages/data-stack/sources.tsx b/src/pages/data-stack/sources.tsx index 75768cda64e2..9bc21081dbfc 100644 --- a/src/pages/data-stack/sources.tsx +++ b/src/pages/data-stack/sources.tsx @@ -4,6 +4,7 @@ import { customerDataInfrastructureNav } from '../../hooks/useCustomerDataInfras import { TreeMenu } from 'components/TreeMenu' import SEO from 'components/seo' import Link from 'components/Link' +import WarehouseWizardHint from 'components/WarehouseWizardHint' import DWInstallationPlatforms from './dw-installation-platforms' const LeftSidebarContent = () => { @@ -19,6 +20,9 @@ export default function Sources(): JSX.Element { image="images/og/cdp.jpg" /> } title="Data sources & import (ELT)"> +
+ +

Connect your external databases, SaaS tools, ad platforms, and more to sync data in bulk into your PostHog warehouse for analysis and modeling. All events and user data captured via PostHog SDKs are diff --git a/src/templates/DataWarehouseSource.tsx b/src/templates/DataWarehouseSource.tsx index bf7e630a5c10..d5648ea1baee 100644 --- a/src/templates/DataWarehouseSource.tsx +++ b/src/templates/DataWarehouseSource.tsx @@ -4,6 +4,7 @@ import SEO from 'components/seo' import ReactMarkdown from 'react-markdown' import ReaderView from 'components/ReaderView' import SourceConfiguration from 'components/Product/Sources/Configuration' +import WarehouseWizardHint from 'components/WarehouseWizardHint' import { getProseClasses } from '../constants' interface SourceField { @@ -43,6 +44,7 @@ export default function DataWarehouseSource({ Beta )}

+

Connect {name} to PostHog to sync your data into the PostHog data warehouse for analysis and