diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index c958ac2a03ee8..eddb4e739bb55 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -5,7 +5,7 @@ import { ActivityLogItem } from 'lib/components/ActivityLog/humanizeActivity' import { apiStatusLogic } from 'lib/logic/apiStatusLogic' import { objectClean, toParams } from 'lib/utils' import posthog from 'posthog-js' -import { NewFeatureForm } from 'scenes/feature-management/featureManagementEditLogic' +import { FeatureForm } from 'scenes/feature-management/featureManagementEditLogic' import { RecordingComment } from 'scenes/session-recordings/player/inspector/playerInspectorLogic' import { SavedSessionRecordingPlaylistsResult } from 'scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic' @@ -1060,7 +1060,7 @@ const api = { return await new ApiRequest().feature(id, teamId).get() }, async create( - feature: NewFeatureForm, + feature: FeatureForm, teamId: TeamType['id'] = ApiConfig.getCurrentTeamId() ): Promise { return await new ApiRequest().features(teamId).create({ data: feature }) @@ -1068,7 +1068,7 @@ const api = { async update( feature: FeatureType, teamId: TeamType['id'] = ApiConfig.getCurrentTeamId() - ): Promise { + ): Promise { return await new ApiRequest().feature(feature.id, teamId).update({ data: feature }) }, }, diff --git a/frontend/src/scenes/feature-management/FeatureManagementEdit.tsx b/frontend/src/scenes/feature-management/FeatureManagementEdit.tsx index a6379a1a3cd5b..d266530c90c48 100644 --- a/frontend/src/scenes/feature-management/FeatureManagementEdit.tsx +++ b/frontend/src/scenes/feature-management/FeatureManagementEdit.tsx @@ -1,4 +1,5 @@ -import { LemonButton, LemonDivider, LemonInput, LemonTextArea } from '@posthog/lemon-ui' +import { IconExternal, IconPlusSmall, IconTrash } from '@posthog/icons' +import { LemonButton, LemonDivider, LemonInput, LemonLabel, LemonModal, LemonTextArea, Link } from '@posthog/lemon-ui' import { useValues } from 'kea' import { Form } from 'kea-forms' import { router } from 'kea-router' @@ -7,7 +8,12 @@ import { LemonField } from 'lib/lemon-ui/LemonField' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' +import { InsightModel } from '~/types' + import { featureManagementEditLogic } from './featureManagementEditLogic' +import { Query } from '~/queries/Query/Query' +import { NodeKind } from '~/queries/schema' +import { commonActionFilterProps } from 'scenes/experiments/Metrics/Selectors' export const scene: SceneExport = { component: FeatureManagementEdit, @@ -18,14 +24,14 @@ export const scene: SceneExport = { } function FeatureManagementEdit(): JSX.Element { - const { props } = useValues(featureManagementEditLogic) + const { props, featureForm } = useValues(featureManagementEditLogic) return (
@@ -88,6 +94,68 @@ function FeatureManagementEdit(): JSX.Element { +
+
+ Success metrics (optional) + {featureForm.success_metrics.map((insight) => ( + alert(`Delete metric ${insight.short_id}`)} + /> + ))} + alert('Add metric')} + icon={} + size="xsmall" + data-attr="add-test-variant" + > + Add metric + +
+
+ Failure metrics (optional) + {featureForm.failure_metrics.map((insight) => ( + alert(`Delete metric ${insight.short_id}`)} + /> + ))} + alert('Add metric')} + icon={} + size="xsmall" + data-attr="add-test-variant" + > + Add metric + +
+
+ Exposure metrics (optional) + {featureForm.exposure_metrics.map((insight) => ( + alert(`Delete metric ${insight.short_id}`)} + /> + ))} + alert('Add metric')} + icon={} + size="xsmall" + data-attr="add-test-variant" + > + Add metric + +
+
+ + +
) } + +function FeatureManagementMetric({ insight, onDelete }: { insight: InsightModel; onDelete: () => void }): JSX.Element { + return ( +
+
+ {insight.name} + + + }> + View metric + + + + } onClick={onDelete} /> +
+
+ ) +} + +function FeatureMetricModal({ + isOpen, + onChange, + onClose, +}: { + isOpen: boolean + onChange: (metric: InsightModel) => void + onClose: () => void +}): JSX.Element { + const { experiment, experimentLoading } = useValues(experimentLogic({ experimentId })) + const { updateExperiment } = useActions(experimentLogic({ experimentId })) + + return ( + + + Cancel + + { + updateExperiment({ + metrics: experiment.metrics, + }) + }} + type="primary" + loading={experimentLoading} + data-attr="create-annotation-submit" + > + Save + +
+ } + > + + + ) +} + +function FeatureMetricPicker({ onChange }: { onChange: (something: any) => void }): JSX.Element { + const hasFilters = (currentTeam?.test_account_filters || []).length > 0 + + if (!editingPrimaryMetricIndex && editingPrimaryMetricIndex !== 0) { + return <> + } + + const metricIdx = editingPrimaryMetricIndex + const currentMetric = experiment.metrics[metricIdx] as ExperimentTrendsQuery + + return ( + <> + ): void => { + const series = actionsAndEventsToSeries( + { actions, events, data_warehouse } as any, + true, + MathAvailability.All + ) + + setTrendsExposureMetric({ + metricIdx, + series, + }) + }} + typeKey="experiment-metric" + buttonCopy="Add graph series" + showSeriesIndicator={true} + entitiesLimit={1} + showNumericalPropsOnly={true} + {...commonActionFilterProps} + /> +
+ { + setTrendsExposureMetric({ + metricIdx, + filterTestAccounts: checked, + }) + }} + fullWidth + /> +
+
+ +
+ + ) +} diff --git a/frontend/src/scenes/feature-management/featureManagementEditLogic.ts b/frontend/src/scenes/feature-management/featureManagementEditLogic.ts index b5ddc63fe6d27..a482de1463e20 100644 --- a/frontend/src/scenes/feature-management/featureManagementEditLogic.ts +++ b/frontend/src/scenes/feature-management/featureManagementEditLogic.ts @@ -7,7 +7,7 @@ import api from 'lib/api' import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { Breadcrumb, FeatureType } from '~/types' +import { Breadcrumb, FeatureType, InsightModel } from '~/types' import type { featureManagementEditLogicType } from './featureManagementEditLogicType' import { featureManagementLogic } from './featureManagementLogic' @@ -17,12 +17,22 @@ export interface FeatureLogicProps { id: string } -export type NewFeatureForm = Pick +export type FeatureForm = { + name: string + key: string + description: string + success_metrics: InsightModel[] + failure_metrics: InsightModel[] + exposure_metrics: InsightModel[] +} -const NEW_FEATURE: NewFeatureForm = { +const NEW_FEATURE: FeatureForm = { key: '', name: '', description: '', + success_metrics: [], + failure_metrics: [], + exposure_metrics: [], } export const featureManagementEditLogic = kea([ @@ -32,23 +42,23 @@ export const featureManagementEditLogic = kea([ actions: [featureManagementLogic, ['loadFeatures']], }), loaders(({ props, actions }) => ({ - feature: { - saveFeature: async (updatedFeature: NewFeatureForm | FeatureType) => { + featureForm: { + saveFeature: async (updatedFeature: FeatureForm): Promise => { let feature if (props.id === 'new') { feature = await api.features.create(updatedFeature) } else { - feature = await api.features.update(updatedFeature as FeatureType) + feature = await api.features.update(updatedFeature) } // Reset the form after creation actions.resetFeature() - return feature + return updatedFeature }, }, })), forms(({ actions }) => ({ - feature: { + featureForm: { defaults: { ...NEW_FEATURE }, // sync validation, will be shown as errors in the form errors: ({ name }) => { @@ -63,7 +73,7 @@ export const featureManagementEditLogic = kea([ }, })), reducers({ - feature: [ + featureForm: [ NEW_FEATURE, { setFeatureValue: (state, { name, value }) => { diff --git a/frontend/src/scenes/feature-management/featureManagementLogic.ts b/frontend/src/scenes/feature-management/featureManagementLogic.ts index 494d2abc14774..d4629afc3b9b5 100644 --- a/frontend/src/scenes/feature-management/featureManagementLogic.ts +++ b/frontend/src/scenes/feature-management/featureManagementLogic.ts @@ -72,6 +72,7 @@ export const featureManagementLogic = kea([ }), listeners(({ actions, values }) => ({ loadFeaturesSuccess: ({ features }) => { + console.log('loadFeaturesSuccess', { features, active: values.activeFeatureId }) if (values.activeFeatureId === null && features.results.length > 0) { actions.setActiveFeatureId(features.results[0].id) } @@ -84,7 +85,9 @@ export const featureManagementLogic = kea([ }), urlToAction(({ actions, values }) => ({ '/features/:id': ({ id }) => { - if (id && String(values.activeFeatureId) !== id && id !== 'new') { + console.log('urlToAction', { id }) + + if (id && String(values.activeFeatureId) !== id) { actions.setActiveFeatureId(id) } },