diff --git a/frontend/src/scenes/appScenes.ts b/frontend/src/scenes/appScenes.ts index 38bf3e1dedd7e..9b0ef2ed521ae 100644 --- a/frontend/src/scenes/appScenes.ts +++ b/frontend/src/scenes/appScenes.ts @@ -23,6 +23,7 @@ export const appScenes: Record any> = { [Scene.PersonsManagement]: () => import('./persons-management/PersonsManagementScene'), [Scene.Person]: () => import('./persons/PersonScene'), [Scene.Pipeline]: () => import('./pipeline/Pipeline'), + [Scene.PipelineApp]: () => import('./pipeline/PipelineApp'), [Scene.Group]: () => import('./groups/Group'), [Scene.Action]: () => import('./actions/Action'), [Scene.Experiments]: () => import('./experiments/Experiments'), diff --git a/frontend/src/scenes/pipeline/Pipeline.stories.tsx b/frontend/src/scenes/pipeline/Pipeline.stories.tsx index 639cf3d1d8cea..97b8f668fa4f7 100644 --- a/frontend/src/scenes/pipeline/Pipeline.stories.tsx +++ b/frontend/src/scenes/pipeline/Pipeline.stories.tsx @@ -3,7 +3,7 @@ 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 { PipelineAppTabs, PipelineTabs } from '~/types' import { pipelineLogic } from './pipelineLogic' import { mswDecorator, useStorybookMocks } from '~/mocks/browser' @@ -58,3 +58,29 @@ export function PipelineTransformationsPage(): JSX.Element { }, []) return } + +export function PipelineAppConfiguration(): JSX.Element { + useEffect(() => { + router.actions.push(urls.pipelineApp(1, PipelineAppTabs.Configuration)) + }, []) + return +} + +export function PipelineAppMetrics(): JSX.Element { + useEffect(() => { + router.actions.push(urls.pipelineApp(1, PipelineAppTabs.Metrics)) + }, []) + return +} + +export function PipelineAppLogs(): JSX.Element { + useStorybookMocks({ + get: { + 'api/projects/:team_id/plugin_configs/1/logs': require('./__mocks__/pluginLogs.json'), + }, + }) + useEffect(() => { + router.actions.push(urls.pipelineApp(1, PipelineAppTabs.Logs)) + }, []) + return +} diff --git a/frontend/src/scenes/pipeline/PipelineApp.tsx b/frontend/src/scenes/pipeline/PipelineApp.tsx new file mode 100644 index 0000000000000..1c9df590dd010 --- /dev/null +++ b/frontend/src/scenes/pipeline/PipelineApp.tsx @@ -0,0 +1,47 @@ +import { SceneExport } from 'scenes/sceneTypes' +import { useValues } from 'kea' +import { pipelineAppLogic } from './pipelineAppLogic' +import { PageHeader } from 'lib/components/PageHeader' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs/LemonTabs' +import { router } from 'kea-router' +import { PipelineAppTabs } from '~/types' +import { urls } from 'scenes/urls' +import { PluginLogs } from 'scenes/plugins/plugin/PluginLogs' +import { Spinner } from '@posthog/lemon-ui' + +export const scene: SceneExport = { + component: PipelineApp, + logic: pipelineAppLogic, + paramsToProps: ({ params: { id } }: { params: { id?: string } }) => ({ id: id ? parseInt(id) : 'new' }), +} + +export function PipelineApp({ id }: { id?: string } = {}): JSX.Element { + const { currentTab } = useValues(pipelineAppLogic) + + const confId = id ? parseInt(id) : undefined + + if (!confId) { + return + } + + const tab_to_content: Record = { + [PipelineAppTabs.Configuration]:
Configuration editing
, + [PipelineAppTabs.Metrics]:
Metrics page
, + [PipelineAppTabs.Logs]: , + } + + return ( +
+ + router.actions.push(urls.pipelineApp(confId, tab as PipelineAppTabs))} + tabs={Object.values(PipelineAppTabs).map((tab) => ({ + label: tab, + key: tab, + content: tab_to_content[tab], + }))} + /> +
+ ) +} diff --git a/frontend/src/scenes/pipeline/Transformations.tsx b/frontend/src/scenes/pipeline/Transformations.tsx index 39768644c6462..701d26056e3c4 100644 --- a/frontend/src/scenes/pipeline/Transformations.tsx +++ b/frontend/src/scenes/pipeline/Transformations.tsx @@ -12,7 +12,7 @@ import { import { useActions, useValues } from 'kea' import { pipelineTransformationsLogic } from './transformationsLogic' import { PluginImage } from 'scenes/plugins/plugin/PluginImage' -import { PipelineTabs, PluginConfigTypeNew, PluginType, ProductKey } from '~/types' +import { PipelineAppTabs, PipelineTabs, PluginConfigTypeNew, PluginType, ProductKey } from '~/types' import { urls } from 'scenes/urls' import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' import { DndContext, DragEndEvent } from '@dnd-kit/core' @@ -100,7 +100,12 @@ export function Transformations(): JSX.Element { return ( <> - + {pluginConfig.name} @@ -153,7 +158,9 @@ export function Transformations(): JSX.Element { } > - + Error @@ -207,7 +214,10 @@ export function Transformations(): JSX.Element { )} @@ -215,7 +225,7 @@ export function Transformations(): JSX.Element { @@ -223,7 +233,7 @@ export function Transformations(): JSX.Element { diff --git a/frontend/src/scenes/pipeline/__mocks__/pluginLogs.json b/frontend/src/scenes/pipeline/__mocks__/pluginLogs.json new file mode 100644 index 0000000000000..09fa96824bf74 --- /dev/null +++ b/frontend/src/scenes/pipeline/__mocks__/pluginLogs.json @@ -0,0 +1,29 @@ +{ + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "id": "018bb51f-0f9f-0000-34ae-d3aa1d9a5770", + "team_id": 1, + "plugin_id": 1, + "plugin_config_id": 11, + "timestamp": "2023-11-09T17:26:33.626000Z", + "source": "PLUGIN", + "type": "ERROR", + "message": "Error: Received an unexpected error from the endpoint API. Response 400: {\"meta\":{\"errors\":[\"value for attribute '$current_url' cannot be longer than 1000 bytes\"]}}\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)", + "instance_id": "12345678-1234-1234-1234-123456789012" + }, + { + "id": "018bb51e-262a-0000-eb34-39afd4691d56", + "team_id": 1, + "plugin_id": 1, + "plugin_config_id": 11, + "timestamp": "2023-11-09T17:25:33.790000Z", + "source": "PLUGIN", + "type": "INFO", + "message": "Successfully sent event to endpoint", + "instance_id": "12345678-1234-1234-1234-123456789012" + } + ] +} diff --git a/frontend/src/scenes/pipeline/pipelineAppLogic.tsx b/frontend/src/scenes/pipeline/pipelineAppLogic.tsx new file mode 100644 index 0000000000000..da93026a47af3 --- /dev/null +++ b/frontend/src/scenes/pipeline/pipelineAppLogic.tsx @@ -0,0 +1,53 @@ +import { kea, reducers, path, props, key, actions, selectors } from 'kea' + +import type { pipelineAppLogicType } from './pipelineAppLogicType' +import { Breadcrumb, PipelineAppTabs } from '~/types' +import { urls } from 'scenes/urls' +import { actionToUrl, urlToAction } from 'kea-router' + +export interface PipelineAppLogicProps { + id: number +} + +export const pipelineAppLogic = kea([ + props({} as PipelineAppLogicProps), + key(({ id }) => id), + path((id) => ['scenes', 'pipeline', 'PipelineAppLogic', id]), + actions({ + setCurrentTab: (tab: PipelineAppTabs = PipelineAppTabs.Configuration) => ({ tab }), + }), + reducers({ + currentTab: [ + PipelineAppTabs.Configuration as PipelineAppTabs, + { + setCurrentTab: (_, { tab }) => tab, + }, + ], + }), + selectors({ + breadcrumbs: [ + () => [], + (): Breadcrumb[] => [ + { + name: 'Pipeline', + path: urls.pipeline(), + }, + { + name: 'App name', + }, + ], + ], + }), + actionToUrl(({ values, props }) => { + return { + setCurrentTab: () => [urls.pipelineApp(props.id, values.currentTab)], + } + }), + urlToAction(({ actions, values }) => ({ + '/pipeline/:id/:tab': ({ tab }) => { + if (tab !== values.currentTab) { + actions.setCurrentTab(tab as PipelineAppTabs) + } + }, + })), +]) diff --git a/frontend/src/scenes/sceneTypes.ts b/frontend/src/scenes/sceneTypes.ts index 5d5ed7a89c3ff..be279d2e22f45 100644 --- a/frontend/src/scenes/sceneTypes.ts +++ b/frontend/src/scenes/sceneTypes.ts @@ -22,6 +22,7 @@ export enum Scene { PersonsManagement = 'PersonsManagement', Person = 'Person', Pipeline = 'Pipeline', + PipelineApp = 'PipelineApp', Group = 'Group', Action = 'Action', Experiments = 'Experiments', diff --git a/frontend/src/scenes/scenes.ts b/frontend/src/scenes/scenes.ts index dc5aa42bb885e..22533e73b9130 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, PipelineTabs, PropertyFilterType, ReplayTabs } from '~/types' +import { InsightShortId, PipelineAppTabs, PipelineTabs, PropertyFilterType, ReplayTabs } from '~/types' import { combineUrl } from 'kea-router' import { getDefaultEventsSceneQuery } from 'scenes/events/defaults' import { EventsQuery } from '~/queries/schema' @@ -112,6 +112,10 @@ export const sceneConfigurations: Partial> = { projectBased: true, name: 'Pipeline', }, + [Scene.PipelineApp]: { + projectBased: true, + name: 'Pipeline app', + }, [Scene.Experiments]: { projectBased: true, name: 'Experiments', @@ -405,10 +409,14 @@ export const routes: Record = { [urls.persons()]: Scene.PersonsManagement, [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), + ...(Object.fromEntries(Object.values(PipelineTabs).map((tab) => [urls.pipeline(tab), Scene.Pipeline])) as Record< + string, + Scene + >), + // One entry for each available tab (key by app config id) + ...(Object.fromEntries( + Object.values(PipelineAppTabs).map((tab) => [urls.pipelineApp(':id', tab), Scene.PipelineApp]) + ) as Record), [urls.groups(':groupTypeIndex')]: Scene.PersonsManagement, [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 7f6fa0c5b03ea..730c4e39d999a 100644 --- a/frontend/src/scenes/urls.ts +++ b/frontend/src/scenes/urls.ts @@ -7,6 +7,7 @@ import { InsightShortId, ReplayTabs, PipelineTabs, + PipelineAppTabs, } from '~/types' import { combineUrl } from 'kea-router' import { ExportOptions } from '~/exporter/types' @@ -95,8 +96,10 @@ export const urls = { personByUUID: (uuid: string, encode: boolean = true): string => encode ? `/persons/${encodeURIComponent(uuid)}` : `/persons/${uuid}`, persons: (): string => '/persons', - pipeline: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : 'destinations'}`, - pipelineNew: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : 'destinations'}/new`, + pipeline: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : PipelineTabs.Destinations}`, + pipelineApp: (id: string | number, tab?: PipelineAppTabs): string => + `/pipeline/${id}/${tab ? tab : PipelineAppTabs.Configuration}`, + pipelineNew: (tab?: PipelineTabs): string => `/pipeline/${tab ? tab : PipelineTabs.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 b66c5c590ec0e..f2a87164cf565 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -526,6 +526,12 @@ export enum PipelineTabs { Destinations = 'destinations', } +export enum PipelineAppTabs { + Configuration = 'configuration', + Logs = 'logs', + Metrics = 'metrics', +} + export enum ProgressStatus { Draft = 'draft', Running = 'running',