diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-filtering-page.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-filtering-page.png
new file mode 100644
index 0000000000000..57b43ed4238ff
Binary files /dev/null and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-filtering-page.png differ
diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-landing-page.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-landing-page.png
index f71305149b638..854c2468d8cfc 100644
Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-landing-page.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-landing-page.png differ
diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-transformations-page.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-transformations-page.png
new file mode 100644
index 0000000000000..4cf705d0bb98b
Binary files /dev/null and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-transformations-page.png differ
diff --git a/frontend/src/scenes/pipeline/Pipeline.stories.tsx b/frontend/src/scenes/pipeline/Pipeline.stories.tsx
index dd0b1641b9e72..e386b2400c0a5 100644
--- a/frontend/src/scenes/pipeline/Pipeline.stories.tsx
+++ b/frontend/src/scenes/pipeline/Pipeline.stories.tsx
@@ -3,6 +3,8 @@ import { Meta } from '@storybook/react'
import { App } from 'scenes/App'
import { router } from 'kea-router'
import { urls } from 'scenes/urls'
+import { PipelineTabs } from '~/types'
+import { pipelineLogic } from './pipelineLogic'
export default {
title: 'Scenes-App/Pipeline',
@@ -11,8 +13,24 @@ export default {
} as Meta
export function PipelineLandingPage(): JSX.Element {
+ // also Destinations page
useEffect(() => {
router.actions.push(urls.pipeline())
+ pipelineLogic.mount()
+ }, [])
+ return
+}
+export function PipelineFilteringPage(): JSX.Element {
+ useEffect(() => {
+ router.actions.push(urls.pipeline(PipelineTabs.Filters))
+ pipelineLogic.mount()
+ }, [])
+ return
+}
+export function PipelineTransformationsPage(): JSX.Element {
+ useEffect(() => {
+ router.actions.push(urls.pipeline(PipelineTabs.Transformations))
+ pipelineLogic.mount()
}, [])
return
}
diff --git a/frontend/src/scenes/pipeline/Pipeline.tsx b/frontend/src/scenes/pipeline/Pipeline.tsx
index 42ee27cc7c66b..29d417fbf614d 100644
--- a/frontend/src/scenes/pipeline/Pipeline.tsx
+++ b/frontend/src/scenes/pipeline/Pipeline.tsx
@@ -1,11 +1,35 @@
import { SceneExport } from 'scenes/sceneTypes'
-import { pipelineLogic } from './pipelineLogic'
import { PageHeader } from 'lib/components/PageHeader'
+import { humanFriendlyTabName, pipelineLogic, singularName } from './pipelineLogic'
+import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
+import { useValues } from 'kea'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { urls } from 'scenes/urls'
+import { router } from 'kea-router'
+import { PipelineTabs } from '~/types'
export function Pipeline(): JSX.Element {
+ const { currentTab } = useValues(pipelineLogic)
+
+ const singular = singularName(currentTab)
return (
-
+
+ New {singular}
+
+ }
+ />
+ router.actions.push(urls.pipeline(tab as PipelineTabs))}
+ tabs={Object.values(PipelineTabs).map((tab) => ({
+ label: humanFriendlyTabName(tab),
+ key: tab,
+ }))}
+ />
)
}
@@ -14,6 +38,3 @@ export const scene: SceneExport = {
component: Pipeline,
logic: pipelineLogic,
}
-
-// TODO: error from import ./pipeline/PipelineScene
-// TODO: update https://storybook.posthog.net/?path=/docs/how-to-build-a-scene--docs <- about kea stuff to exclude and have run pnpm ... for type creation
diff --git a/frontend/src/scenes/pipeline/pipelineLogic.tsx b/frontend/src/scenes/pipeline/pipelineLogic.tsx
index 9d74a469558e0..017e1966745b7 100644
--- a/frontend/src/scenes/pipeline/pipelineLogic.tsx
+++ b/frontend/src/scenes/pipeline/pipelineLogic.tsx
@@ -1,6 +1,67 @@
-import { kea } from 'kea'
+import { actions, kea, path, reducers, selectors } from 'kea'
import type { pipelineLogicType } from './pipelineLogicType'
+import { actionToUrl, urlToAction } from 'kea-router'
+import { urls } from 'scenes/urls'
+import { Breadcrumb, PipelineTabs } from '~/types'
-export const pipelineLogic = kea({
- path: ['scenes', 'pipeline', 'pipelineLogic'],
-})
+export const singularName = (tab: PipelineTabs): string => {
+ switch (tab) {
+ case PipelineTabs.Filters:
+ return 'filter'
+ case PipelineTabs.Transformations:
+ return 'transformation'
+ case PipelineTabs.Destinations:
+ return 'destination'
+ }
+}
+
+export const humanFriendlyTabName = (tab: PipelineTabs): string => {
+ switch (tab) {
+ case PipelineTabs.Filters:
+ return 'Filters'
+ case PipelineTabs.Transformations:
+ return 'Transformations'
+ case PipelineTabs.Destinations:
+ return 'Destinations'
+ }
+}
+
+export const pipelineLogic = kea([
+ path(['scenes', 'pipeline', 'pipelineLogic']),
+ actions({
+ setCurrentTab: (tab: PipelineTabs = PipelineTabs.Destinations) => ({ tab }),
+ }),
+ reducers({
+ currentTab: [
+ PipelineTabs.Destinations as PipelineTabs,
+ {
+ setCurrentTab: (_, { tab }) => tab,
+ },
+ ],
+ }),
+ selectors(() => ({
+ breadcrumbs: [
+ (s) => [s.currentTab],
+ (tab): Breadcrumb[] => {
+ const breadcrumbs: Breadcrumb[] = [{ name: 'Pipeline' }]
+ breadcrumbs.push({
+ name: humanFriendlyTabName(tab),
+ })
+
+ return breadcrumbs
+ },
+ ],
+ })),
+ actionToUrl(({ values }) => {
+ return {
+ setCurrentTab: () => [urls.pipeline(values.currentTab)],
+ }
+ }),
+ urlToAction(({ actions, values }) => ({
+ '/pipeline/:tab': ({ tab }) => {
+ if (tab !== values.currentTab) {
+ actions.setCurrentTab(tab as PipelineTabs)
+ }
+ },
+ })),
+])
diff --git a/frontend/src/scenes/scenes.ts b/frontend/src/scenes/scenes.ts
index 311a72f6f5c34..78153fac0cf1e 100644
--- a/frontend/src/scenes/scenes.ts
+++ b/frontend/src/scenes/scenes.ts
@@ -3,7 +3,7 @@ import { Error404 as Error404Component } from '~/layout/Error404'
import { ErrorNetwork as ErrorNetworkComponent } from '~/layout/ErrorNetwork'
import { ErrorProjectUnavailable as ErrorProjectUnavailableComponent } from '~/layout/ErrorProjectUnavailable'
import { urls } from 'scenes/urls'
-import { InsightShortId, PropertyFilterType, ReplayTabs } from '~/types'
+import { InsightShortId, PipelineTabs, PropertyFilterType, ReplayTabs } from '~/types'
import { combineUrl } from 'kea-router'
import { getDefaultEventsSceneQuery } from 'scenes/events/defaults'
import { EventsQuery } from '~/queries/schema'
@@ -437,6 +437,11 @@ export const routes: Record = {
[urls.personByUUID('*', false)]: Scene.Person,
[urls.persons()]: Scene.Persons,
[urls.pipeline()]: Scene.Pipeline,
+ // One entry for every available tab
+ ...Object.values(PipelineTabs).reduce((acc, tab) => {
+ acc[urls.pipeline(tab)] = Scene.Pipeline
+ return acc
+ }, {} as Record),
[urls.groups(':groupTypeIndex')]: Scene.Groups,
[urls.group(':groupTypeIndex', ':groupKey', false)]: Scene.Group,
[urls.group(':groupTypeIndex', ':groupKey', false, ':groupTab')]: Scene.Group,
diff --git a/frontend/src/scenes/urls.ts b/frontend/src/scenes/urls.ts
index 9e454a67f9f81..a0253f81ffc21 100644
--- a/frontend/src/scenes/urls.ts
+++ b/frontend/src/scenes/urls.ts
@@ -6,6 +6,7 @@ import {
FilterType,
InsightShortId,
ReplayTabs,
+ PipelineTabs,
} from '~/types'
import { combineUrl } from 'kea-router'
import { ExportOptions } from '~/exporter/types'
@@ -93,7 +94,8 @@ export const urls = {
personByUUID: (uuid: string, encode: boolean = true): string =>
encode ? `/persons/${encodeURIComponent(uuid)}` : `/persons/${uuid}`,
persons: (): string => '/persons',
- pipeline: (): string => '/pipeline',
+ pipeline: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : 'destinations'}`,
+ pipelineNew: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : 'destinations'}/new`,
groups: (groupTypeIndex: string | number): string => `/groups/${groupTypeIndex}`,
// :TRICKY: Note that groupKey is provided by user. We need to override urlPatternOptions for kea-router.
group: (groupTypeIndex: string | number, groupKey: string, encode: boolean = true, tab?: string | null): string =>
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 00a73ed0b8cf3..21c2465b8e0d7 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -515,6 +515,12 @@ export enum ExperimentsTabs {
Archived = 'archived',
}
+export enum PipelineTabs {
+ Filters = 'filters',
+ Transformations = 'transformations',
+ Destinations = 'destinations',
+}
+
export enum ProgressStatus {
Draft = 'draft',
Running = 'running',