- {'Need to control your costs? Learn about ways to '}
+ {'Are you looking to control your costs? Learn about ways to '}
([
setShowTierBreakdown: (showTierBreakdown: boolean) => ({ showTierBreakdown }),
toggleIsPricingModalOpen: true,
toggleIsPlanComparisonModalOpen: (highlightedFeatureKey?: string) => ({ highlightedFeatureKey }),
- setSurveyResponse: (surveyResponse: string, key: string) => ({ surveyResponse, key }),
+ setSurveyResponse: (key: string, value: string | string[]) => ({ key, value }),
+ toggleSurveyReason: (reason: string) => ({ reason }),
reportSurveyShown: (surveyID: string, productType: string) => ({ surveyID, productType }),
- reportSurveySent: (surveyID: string, surveyResponse: Record) => ({
+ reportSurveySent: (surveyID: string, surveyResponse: Record) => ({
surveyID,
surveyResponse,
}),
@@ -110,10 +111,19 @@ export const billingProductLogic = kea([
},
],
surveyResponse: [
- {},
+ { $survey_response_2: [], $survey_response: '' } as {
+ $survey_response_2: string[]
+ $survey_response: string
+ },
{
- setSurveyResponse: (state, { surveyResponse, key }) => {
- return { ...state, [key]: surveyResponse }
+ setSurveyResponse: (state, { key, value }) => {
+ return { ...state, [key]: value }
+ },
+ toggleSurveyReason: (state, { reason }) => {
+ const reasons = state.$survey_response_2.includes(reason)
+ ? state.$survey_response_2.filter((r) => r !== reason)
+ : [...state.$survey_response_2, reason]
+ return { ...state, $survey_response_2: reasons }
},
},
],
@@ -146,10 +156,11 @@ export const billingProductLogic = kea([
customLimitUsd: [
(s, p) => [s.billing, p.product],
(billing, product) => {
- return (
- billing?.custom_limits_usd?.[product.type] ||
- (product.usage_key ? billing?.custom_limits_usd?.[product.usage_key] : null)
- )
+ const customLimit = billing?.custom_limits_usd?.[product.type]
+ if (customLimit === 0 || customLimit) {
+ return customLimit
+ }
+ return product.usage_key ? billing?.custom_limits_usd?.[product.usage_key] : null
},
],
currentAndUpgradePlans: [
@@ -255,7 +266,7 @@ export const billingProductLogic = kea([
billingLoaded: () => {
actions.setIsEditingBillingLimit(false)
actions.setBillingLimitInput(
- values.customLimitUsd
+ values.customLimitUsd === 0 || values.customLimitUsd
? parseInt(
typeof values.customLimitUsd === 'number'
? `${values.customLimitUsd}`
@@ -273,6 +284,11 @@ export const billingProductLogic = kea([
actions.setSurveyID(surveyID)
},
reportSurveySent: ({ surveyID, surveyResponse }) => {
+ // @note(zach): this is submitting to https://us.posthog.com/project/2/surveys/018b6e13-590c-0000-decb-c727a2b3f462?edit=true
+ // $survey_response: open text response
+ // $survey_response_1: this is the product type
+ // $survey_response_2: list of reasons
+ // The order is due to the form being built before reasons we're supported. Please do not change the order.
posthog.capture('survey sent', {
$survey_id: surveyID,
...surveyResponse,
@@ -286,11 +302,8 @@ export const billingProductLogic = kea([
actions.setSurveyID('')
},
deactivateProductSuccess: async (_, breakpoint) => {
- if (!values.unsubscribeError) {
- const textAreaNotEmpty = values.surveyResponse['$survey_response']?.length > 0
- textAreaNotEmpty
- ? actions.reportSurveySent(values.surveyID, values.surveyResponse)
- : actions.reportSurveyDismissed(values.surveyID)
+ if (!values.unsubscribeError && values.surveyID) {
+ actions.reportSurveySent(values.surveyID, values.surveyResponse)
}
await breakpoint(200)
location.reload()
@@ -378,7 +391,7 @@ export const billingProductLogic = kea([
return
}
actions.updateBillingLimits({
- [props.product.type]: input,
+ [props.product.type]: typeof input === 'number' ? `${input}` : null,
})
},
options: {
diff --git a/frontend/src/scenes/cohorts/CohortFilters/constants.tsx b/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
index e1f5c7b53bfbd..5db9872a4cca5 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
@@ -39,6 +39,7 @@ import {
FilterLogicalOperator,
PropertyMathType,
PropertyOperator,
+ SingleFieldDateType,
TimeUnitType,
ValueOptionType,
} from '~/types'
@@ -200,6 +201,21 @@ export const FIELD_VALUES: Record = {
},
},
},
+ [FieldOptionsType.SingleFieldDateOperators]: {
+ label: 'Date Operators',
+ type: FieldOptionsType.SingleFieldDateOperators,
+ values: {
+ [SingleFieldDateType.IsDateExact]: {
+ label: 'on the date',
+ },
+ [SingleFieldDateType.IsDateAfter]: {
+ label: 'since',
+ },
+ [SingleFieldDateType.IsDateBefore]: {
+ label: 'before',
+ },
+ },
+ },
[FieldOptionsType.MathOperators]: {
label: 'Operators',
type: FieldOptionsType.MathOperators,
@@ -861,7 +877,12 @@ export const renderField: Record JSX.El
return
},
[FilterType.MathOperator]: function _renderField(p) {
- return
+ return (
+
+ )
},
[FilterType.EventsAndActionsMathOperator]: function _renderField(p) {
return
diff --git a/frontend/src/scenes/cohorts/CohortFilters/types.ts b/frontend/src/scenes/cohorts/CohortFilters/types.ts
index 2b9f753b15558..590e553c5767a 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/types.ts
+++ b/frontend/src/scenes/cohorts/CohortFilters/types.ts
@@ -42,6 +42,7 @@ export enum FieldOptionsType {
LifecycleBehavioral = 'lifecycleBehavioral',
TimeUnits = 'timeUnits',
DateOperators = 'dateOperators',
+ SingleFieldDateOperators = 'singleFieldDateOperators',
MathOperators = 'mathOperators',
ValueOptions = 'valueOptions',
EventsAndActionsMathOperators = 'eventsAndActionsMathOperators',
diff --git a/frontend/src/scenes/dashboard/DashboardItems.tsx b/frontend/src/scenes/dashboard/DashboardItems.tsx
index 7ff189ecb5b75..4d74b142e26fa 100644
--- a/frontend/src/scenes/dashboard/DashboardItems.tsx
+++ b/frontend/src/scenes/dashboard/DashboardItems.tsx
@@ -135,11 +135,11 @@ export function DashboardItems(): JSX.Element {
removeFromDashboard: () => removeTile(tile),
}
- if (insight && legacyInsight) {
+ if (insight) {
return (
([
insight,
dashboardId,
queryId,
- 'async',
+ 'force_cache',
methodOptions
)
dashboardsModel.actions.updateDashboardInsight(polledInsight)
diff --git a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
index 248ca1a85bf2b..0df671d07f3bd 100644
--- a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
+++ b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
@@ -248,12 +248,6 @@ const meta: Meta = {
],
}
export default meta
-export function Database(): JSX.Element {
- useEffect(() => {
- router.actions.push(urls.database())
- }, [])
- return
-}
export function IngestionWarnings(): JSX.Element {
setFeatureFlags([FEATURE_FLAGS.INGESTION_WARNINGS_ENABLED])
diff --git a/frontend/src/scenes/data-management/DataManagementScene.tsx b/frontend/src/scenes/data-management/DataManagementScene.tsx
index 6a7e22df47752..07661267bf164 100644
--- a/frontend/src/scenes/data-management/DataManagementScene.tsx
+++ b/frontend/src/scenes/data-management/DataManagementScene.tsx
@@ -6,7 +6,6 @@ import { PageHeader } from 'lib/components/PageHeader'
import { TitleWithIcon } from 'lib/components/TitleWithIcon'
import { FEATURE_FLAGS } from 'lib/constants'
import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
-import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { capitalizeFirstLetter } from 'lib/utils'
@@ -20,7 +19,6 @@ import { urls } from 'scenes/urls'
import { ActivityScope, Breadcrumb } from '~/types'
import { ActionsTable } from './actions/ActionsTable'
-import { DatabaseTableList } from './database/DatabaseTableList'
import type { dataManagementSceneLogicType } from './DataManagementSceneType'
import { EventDefinitionsTable } from './events/EventDefinitionsTable'
import { IngestionWarningsView } from './ingestion-warnings/IngestionWarningsView'
@@ -33,7 +31,6 @@ export enum DataManagementTab {
Annotations = 'annotations',
History = 'history',
IngestionWarnings = 'warnings',
- Database = 'database',
}
const tabs: Record<
@@ -97,18 +94,6 @@ const tabs: Record<
label: 'Ingestion warnings',
content: ,
},
- [DataManagementTab.Database]: {
- url: urls.database(),
- label: (
- <>
- Database
-
- Beta
-
- >
- ),
- content: ,
- },
}
const dataManagementSceneLogic = kea([
diff --git a/frontend/src/scenes/data-management/actions/ActionsTable.tsx b/frontend/src/scenes/data-management/actions/ActionsTable.tsx
index b1658cc94ed83..a0164fad09672 100644
--- a/frontend/src/scenes/data-management/actions/ActionsTable.tsx
+++ b/frontend/src/scenes/data-management/actions/ActionsTable.tsx
@@ -155,8 +155,8 @@ export function ActionsTable(): JSX.Element {
Edit
-
- Copy
+
+ Duplicate
> = {
- [DataWarehouseTab.Explore]: ,
- [DataWarehouseTab.ManagedSources]: ,
- [DataWarehouseTab.SelfManagedSources]: ,
-}
-
-export const humanFriendlyDataWarehouseTabName = (tab: DataWarehouseTab): string => {
- switch (tab) {
- case DataWarehouseTab.Explore:
- return 'Explore'
- case DataWarehouseTab.ManagedSources:
- return 'Managed sources'
- case DataWarehouseTab.SelfManagedSources:
- return 'Self-Managed sources'
- }
+ logic: dataWarehouseExternalSceneLogic,
}
export function DataWarehouseExternalScene(): JSX.Element {
- const { currentTab } = useValues(dataWarehouseSceneLogic)
+ const { viewLoading } = useValues(dataWarehouseExternalSceneLogic)
- const { insightSaving } = useValues(
- insightLogic({
- dashboardItemId: 'new-dataWarehouse',
- cachedInsight: null,
- })
- )
- const { saveAs } = useActions(
- insightLogic({
- dashboardItemId: 'new-dataWarehouse',
- cachedInsight: null,
- })
- )
+ const logic = insightLogic({
+ dashboardItemId: DATAWAREHOUSE_EDITOR_ITEM_ID,
+ cachedInsight: null,
+ })
+ const { insightSaving, insightProps } = useValues(logic)
+ const { saveAs } = useActions(logic)
+
+ const insightDataLogicProps = {
+ ...insightProps,
+ }
return (
- {currentTab === DataWarehouseTab.Explore && (
- saveAs(true)}
- loading={insightSaving}
- >
- Save as insight
-
- )}
-
saveAs(true, false)}
+ loading={insightSaving}
>
- Link source
+ Save as insight
+
+
+ Manage sources
>
}
@@ -91,27 +61,13 @@ export function DataWarehouseExternalScene(): JSX.Element {
}
/>
- router.actions.push(urls.dataWarehouse(tab as DataWarehouseTab))}
- tabs={Object.entries(tabToContent).map(([tab, content]) => ({
- label: (
-
- {humanFriendlyDataWarehouseTabName(tab as DataWarehouseTab)}{' '}
-
- ),
- key: tab,
- content: content,
- }))}
- />
+ {viewLoading ? (
+
+ ) : (
+
+
+
+ )}
)
}
-
-function Explore(): JSX.Element {
- return (
-
-
-
- )
-}
diff --git a/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx b/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx
index 0c41c02b7dc6d..bbb258292bff6 100644
--- a/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx
+++ b/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx
@@ -11,27 +11,20 @@ import { insightLogic } from 'scenes/insights/insightLogic'
import { urls } from 'scenes/urls'
import { Query } from '~/queries/Query/Query'
-import { DatabaseSchemaTable, NodeKind } from '~/queries/schema'
+import { DatabaseSchemaTable } from '~/queries/schema'
+import { InsightLogicProps } from '~/types'
+import { dataWarehouseSceneLogic } from '../settings/dataWarehouseSceneLogic'
import { viewLinkLogic } from '../viewLinkLogic'
import { ViewLinkModal } from '../ViewLinkModal'
-import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic'
import { DeleteTableModal, TableData } from './TableData'
-export const DataWarehouseTables = (): JSX.Element => {
- // insightLogic
- const logic = insightLogic({
- dashboardItemId: 'new-dataWarehouse',
- cachedInsight: null,
- })
- const { insightProps } = useValues(logic)
- // insightDataLogic
- const { query } = useValues(
- insightDataLogic({
- ...insightProps,
- })
- )
+interface DataWarehousetTablesProps {
+ insightProps: InsightLogicProps
+}
+export const DataWarehouseTables = ({ insightProps }: DataWarehousetTablesProps): JSX.Element => {
+ const { query } = useValues(insightDataLogic(insightProps))
const { setQuery: setInsightQuery } = useActions(insightDataLogic(insightProps))
return (
@@ -130,12 +123,7 @@ export const DatabaseTableTreeWithItems = ({ inline }: DatabaseTableTreeProps):
{table.type == 'view' && (
{
- router.actions.push(
- urls.dataWarehouseView(table.id, {
- kind: NodeKind.DataVisualizationNode,
- source: table.query,
- })
- )
+ router.actions.push(urls.dataWarehouseView(table.id))
}}
data-attr="schema-list-item-edit"
fullWidth
diff --git a/frontend/src/scenes/data-warehouse/external/TableData.tsx b/frontend/src/scenes/data-warehouse/external/TableData.tsx
index 2daa2c4a8e3e9..793d003b9fe21 100644
--- a/frontend/src/scenes/data-warehouse/external/TableData.tsx
+++ b/frontend/src/scenes/data-warehouse/external/TableData.tsx
@@ -8,7 +8,7 @@ import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable'
import { DatabaseSchemaTable } from '~/queries/schema'
-import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic'
+import { dataWarehouseSceneLogic } from '../settings/dataWarehouseSceneLogic'
export function TableData(): JSX.Element {
const {
diff --git a/frontend/src/scenes/data-warehouse/external/dataWarehouseExternalSceneLogic.ts b/frontend/src/scenes/data-warehouse/external/dataWarehouseExternalSceneLogic.ts
new file mode 100644
index 0000000000000..3975f1dde3bd9
--- /dev/null
+++ b/frontend/src/scenes/data-warehouse/external/dataWarehouseExternalSceneLogic.ts
@@ -0,0 +1,115 @@
+import { lemonToast } from '@posthog/lemon-ui'
+import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea'
+import { router, urlToAction } from 'kea-router'
+import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic'
+import { insightDataLogic } from 'scenes/insights/insightDataLogic'
+import { insightSceneLogic } from 'scenes/insights/insightSceneLogic'
+import { Scene } from 'scenes/sceneTypes'
+import { urls } from 'scenes/urls'
+
+import { DataVisualizationNode, NodeKind } from '~/queries/schema'
+import { Breadcrumb, InsightShortId, ItemMode } from '~/types'
+
+import type { dataWarehouseExternalSceneLogicType } from './dataWarehouseExternalSceneLogicType'
+
+export const DATAWAREHOUSE_EDITOR_ITEM_ID = 'new-SQL'
+
+export const dataWarehouseExternalSceneLogic = kea([
+ path(() => ['scenes', 'data-warehouse', 'external', 'dataWarehouseExternalSceneLogic']),
+ connect(() => ({
+ values: [databaseTableListLogic, ['viewsMapById', 'database', 'databaseLoading']],
+ actions: [
+ insightSceneLogic,
+ ['setSceneState'],
+ databaseTableListLogic,
+ ['loadDatabase', 'loadDatabaseSuccess'],
+ ],
+ logic: [
+ insightDataLogic({
+ dashboardItemId: DATAWAREHOUSE_EDITOR_ITEM_ID,
+ cachedInsight: null,
+ }),
+ ],
+ })),
+ actions({
+ loadView: (id: string) => ({ id }),
+ setViewLoading: (viewLoading: boolean) => ({ viewLoading }),
+ }),
+ selectors(() => ({
+ breadcrumbs: [
+ () => [],
+ (): Breadcrumb[] => [
+ {
+ key: Scene.DataWarehouse,
+ name: 'Explore',
+ path: urls.dataWarehouse(),
+ },
+ ],
+ ],
+ })),
+ reducers({
+ viewLoading: [
+ false,
+ {
+ loadView: () => true,
+ setViewLoading: (_, { viewLoading }) => viewLoading,
+ },
+ ],
+ }),
+ listeners(({ values, actions }) => ({
+ loadDatabaseSuccess: () => {
+ if (router.values.currentLocation.pathname.includes('/data-warehouse/view')) {
+ router.actions.push(router.values.currentLocation.pathname)
+ }
+ },
+ loadView: async ({ id }) => {
+ if (id && id in values.viewsMapById) {
+ insightDataLogic
+ .findMounted({
+ dashboardItemId: DATAWAREHOUSE_EDITOR_ITEM_ID,
+ cachedInsight: null,
+ })
+ ?.actions.setQuery({
+ kind: NodeKind.DataVisualizationNode,
+ source: values.viewsMapById[id].query,
+ } as DataVisualizationNode)
+ } else {
+ await databaseTableListLogic.asyncActions.loadDatabase()
+
+ if (id && id in values.viewsMapById) {
+ insightDataLogic
+ .findMounted({
+ dashboardItemId: DATAWAREHOUSE_EDITOR_ITEM_ID,
+ cachedInsight: null,
+ })
+ ?.actions.setQuery({
+ kind: NodeKind.DataVisualizationNode,
+ source: values.viewsMapById[id].query,
+ } as DataVisualizationNode)
+ } else {
+ lemonToast.error('Error retrieving view')
+ router.actions.push(urls.dataWarehouse())
+ }
+ }
+
+ actions.setViewLoading(false)
+ },
+ })),
+ urlToAction(({ actions }) => ({
+ '/data-warehouse': () => {
+ insightSceneLogic.actions.setSceneState(
+ String('new-dataWarehouse') as InsightShortId,
+ ItemMode.Edit,
+ undefined
+ )
+ },
+ '/data-warehouse/view/:id': ({ id }) => {
+ insightSceneLogic.actions.setSceneState(
+ String('new-dataWarehouse') as InsightShortId,
+ ItemMode.Edit,
+ undefined
+ )
+ id && actions.loadView(id)
+ },
+ })),
+])
diff --git a/frontend/src/scenes/data-warehouse/new/NewSourceWizard.tsx b/frontend/src/scenes/data-warehouse/new/NewSourceWizard.tsx
index 4f402724ff9b3..e3c29b8afbb37 100644
--- a/frontend/src/scenes/data-warehouse/new/NewSourceWizard.tsx
+++ b/frontend/src/scenes/data-warehouse/new/NewSourceWizard.tsx
@@ -50,9 +50,18 @@ interface NewSourcesWizardProps {
export function NewSourcesWizard({ onComplete }: NewSourcesWizardProps): JSX.Element {
const wizardLogic = sourceWizardLogic({ onComplete })
- const { modalTitle, modalCaption, isWrapped } = useValues(wizardLogic)
+ const {
+ modalTitle,
+ modalCaption,
+ isWrapped,
+ currentStep,
+ isLoading,
+ canGoBack,
+ canGoNext,
+ nextButtonText,
+ showSkipButton,
+ } = useValues(wizardLogic)
const { onBack, onSubmit } = useActions(wizardLogic)
- const { currentStep, isLoading, canGoBack, canGoNext, nextButtonText, showSkipButton } = useValues(wizardLogic)
const { tableLoading: manualLinkIsLoading } = useValues(dataWarehouseTableLogic)
const footer = useCallback(() => {
@@ -93,31 +102,25 @@ export function NewSourcesWizard({ onComplete }: NewSourcesWizardProps): JSX.Ele
<>
{modalTitle}
{modalCaption}
-
-
-
-
+
+ {currentStep === 1 ? (
+
+ ) : currentStep === 2 ? (
+
+ ) : currentStep === 3 ? (
+
+ ) : currentStep === 4 ? (
+
+ ) : (
+ Something went wrong...
+ )}
+
{footer()}
>
>
)
}
-interface ModalPageProps {
- page: number
- children?: React.ReactNode
-}
-
-function ModalPage({ children, page }: ModalPageProps): JSX.Element {
- const { currentStep } = useValues(sourceWizardLogic)
-
- if (currentStep !== page) {
- return <>>
- }
-
- return {children}
-}
-
function FirstStep(): JSX.Element {
const { connectors, manualConnectors, addToHubspotButtonUrl } = useValues(sourceWizardLogic)
const { selectConnector, toggleManualLinkFormVisible, onNext, setManualLinkingProvider } =
@@ -138,7 +141,7 @@ function FirstStep(): JSX.Element {
}
return (
-
+ <>
Managed by PostHog
@@ -224,32 +227,20 @@ function FirstStep(): JSX.Element {
},
]}
/>
-
+ >
)
}
function SecondStep(): JSX.Element {
const { selectedConnector } = useValues(sourceWizardLogic)
- return (
-
- {selectedConnector ? : }
-
- )
+ return selectedConnector ? :
}
function ThirdStep(): JSX.Element {
- return (
-
-
-
- )
+ return
}
function FourthStep(): JSX.Element {
- return (
-
-
-
- )
+ return
}
diff --git a/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx b/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
index b0216397540e9..502d814005534 100644
--- a/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
+++ b/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
@@ -10,7 +10,6 @@ import { urls } from 'scenes/urls'
import {
Breadcrumb,
- DataWarehouseTab,
ExternalDataSourceCreatePayload,
ExternalDataSourceSyncSchema,
ExternalDataSourceType,
@@ -65,7 +64,7 @@ export const SOURCE_DETAILS: Record = {
Hubspot: {
name: 'Hubspot',
fields: [],
- caption: '',
+ caption: 'Succesfully authenticated with Hubspot. Please continue here to complete the source setup',
},
Postgres: {
name: 'Postgres',
@@ -843,12 +842,7 @@ export const sourceWizardLogic = kea([
},
closeWizard: () => {
actions.cancelWizard()
-
- if (router.values.location.pathname.includes(urls.dataWarehouseTable())) {
- router.actions.push(urls.dataWarehouse(DataWarehouseTab.ManagedSources))
- } else if (router.values.location.pathname.includes(urls.pipelineNodeDataWarehouseNew())) {
- router.actions.push(urls.pipeline(PipelineTab.DataImport))
- }
+ router.actions.push(urls.pipeline(PipelineTab.Sources))
},
cancelWizard: () => {
actions.onClear()
diff --git a/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseViewsLogic.tsx b/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseViewsLogic.tsx
index dcdc794753329..c003d47acec9c 100644
--- a/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseViewsLogic.tsx
+++ b/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseViewsLogic.tsx
@@ -1,3 +1,4 @@
+import { lemonToast } from '@posthog/lemon-ui'
import { connect, kea, listeners, path, selectors } from 'kea'
import { loaders } from 'kea-loaders'
import { router } from 'kea-router'
@@ -22,7 +23,11 @@ export const dataWarehouseViewsLogic = kea([
null,
{
createDataWarehouseSavedQuery: async (view: Partial) => {
- await api.dataWarehouseSavedQueries.create(view)
+ const newView = await api.dataWarehouseSavedQueries.create(view)
+
+ lemonToast.success(`${newView.name ?? 'View'} successfully created`)
+ router.actions.push(urls.dataWarehouseView(newView.id))
+
return null
},
deleteDataWarehouseSavedQuery: async (viewId: string) => {
@@ -39,7 +44,6 @@ export const dataWarehouseViewsLogic = kea([
listeners(({ actions }) => ({
createDataWarehouseSavedQuerySuccess: () => {
actions.loadDatabase()
- router.actions.push(urls.dataWarehouse())
},
updateDataWarehouseSavedQuerySuccess: () => {
actions.loadDatabase()
diff --git a/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx b/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx
index 0c97d1bd088f8..9391e0cba5ada 100644
--- a/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx
+++ b/frontend/src/scenes/data-warehouse/settings/DataWarehouseManagedSourcesTable.tsx
@@ -1,8 +1,6 @@
import { TZLabel } from '@posthog/apps-common'
import { LemonButton, LemonDialog, LemonTable, LemonTag, Link, Spinner, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
-import { router } from 'kea-router'
-import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
import { More } from 'lib/lemon-ui/LemonButton/More'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import IconAwsS3 from 'public/services/aws-s3.png'
@@ -17,7 +15,7 @@ import IconStripe from 'public/services/stripe.png'
import IconZendesk from 'public/services/zendesk.png'
import { urls } from 'scenes/urls'
-import { DataWarehouseTab, manualLinkSources, ProductKey } from '~/types'
+import { manualLinkSources, PipelineNodeTab, PipelineStage } from '~/types'
import { dataWarehouseSettingsLogic } from './dataWarehouseSettingsLogic'
@@ -33,25 +31,12 @@ export function DataWarehouseManagedSourcesTable(): JSX.Element {
useValues(dataWarehouseSettingsLogic)
const { deleteSource, reloadSource } = useActions(dataWarehouseSettingsLogic)
- if (!dataWarehouseSourcesLoading && dataWarehouseSources?.results.length === 0) {
- return (
- router.actions.push(urls.pipelineNodeDataWarehouseNew())}
- />
- )
- }
-
return (
diff --git a/frontend/src/scenes/data-warehouse/settings/DataWarehouseSelfManagedSourcesTable.tsx b/frontend/src/scenes/data-warehouse/settings/DataWarehouseSelfManagedSourcesTable.tsx
index a067aff2bd2c9..19c15e4de1c58 100644
--- a/frontend/src/scenes/data-warehouse/settings/DataWarehouseSelfManagedSourcesTable.tsx
+++ b/frontend/src/scenes/data-warehouse/settings/DataWarehouseSelfManagedSourcesTable.tsx
@@ -12,6 +12,7 @@ export function DataWarehouseSelfManagedSourcesTable(): JSX.Element {
return (
([
path(['scenes', 'warehouse', 'dataWarehouseSceneLogic']),
+ props({} as DataWarehouseSceneLogicProps),
connect(() => ({
values: [
databaseTableListLogic,
@@ -28,7 +35,6 @@ export const dataWarehouseSceneLogic = kea([
})),
actions(({ values }) => ({
selectRow: (row: DatabaseSchemaTable | null) => ({ row }),
- setSceneTab: (tab: DataWarehouseTab) => ({ tab }),
setIsEditingSavedQuery: (isEditingSavedQuery: boolean) => ({ isEditingSavedQuery }),
toggleEditSchemaMode: (inEditSchemaMode?: boolean) => ({ inEditSchemaMode }),
updateSelectedSchema: (columnKey: string, columnType: DatabaseSerializedFieldType) => ({
@@ -40,7 +46,7 @@ export const dataWarehouseSceneLogic = kea([
cancelEditSchema: () => ({ database: values.database }),
deleteDataWarehouseTable: (tableId: string) => ({ tableId }),
toggleSchemaModal: true,
- setEditingView: (id: string) => ({ id }),
+ setEditingView: (id: string | null) => ({ id }),
updateView: (query: string) => ({ query }),
})),
reducers({
@@ -140,12 +146,6 @@ export const dataWarehouseSceneLogic = kea([
setEditingView: (_, { id }) => id,
},
],
- currentTab: [
- DataWarehouseTab.Explore as DataWarehouseTab,
- {
- setSceneTab: (_, { tab }) => tab,
- },
- ],
}),
selectors({
dataWarehouseTablesBySourceType: [
@@ -185,10 +185,9 @@ export const dataWarehouseSceneLogic = kea([
actions.setIsEditingSavedQuery(false)
},
updateDataWarehouseSavedQuerySuccess: async ({ payload }) => {
- actions.setIsEditingSavedQuery(false)
lemonToast.success(`${payload?.name ?? 'View'} successfully updated`)
if (payload) {
- router.actions.push(urls.dataWarehouseView(payload.id, payload.query))
+ router.actions.push(urls.dataWarehouseView(payload.id))
}
},
saveSchema: async () => {
@@ -262,14 +261,12 @@ export const dataWarehouseSceneLogic = kea([
}
},
})),
- urlToAction(({ actions, values }) => ({
+ urlToAction(({ actions }) => ({
'/data-warehouse/view/:id': ({ id }) => {
actions.setEditingView(id as string)
},
- '/data-warehouse/:tab': ({ tab }) => {
- if (tab !== values.currentTab) {
- actions.setSceneTab(tab as DataWarehouseTab)
- }
+ '/data-warehouse': () => {
+ actions.setEditingView(null)
},
})),
])
diff --git a/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts b/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts
index ee76fff2447a6..32a1925dd3da2 100644
--- a/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts
+++ b/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts
@@ -7,7 +7,7 @@ import posthog from 'posthog-js'
import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic'
import { DatabaseSchemaDataWarehouseTable } from '~/queries/schema'
-import { DataWarehouseSettingsTab, DataWarehouseTab, ExternalDataSourceSchema, ExternalDataStripeSource } from '~/types'
+import { DataWarehouseSettingsTab, ExternalDataSourceSchema, ExternalDataStripeSource } from '~/types'
import type { dataWarehouseSettingsLogicType } from './dataWarehouseSettingsLogicType'
@@ -36,7 +36,6 @@ export const dataWarehouseSettingsLogic = kea([
sourceLoadingFinished: (source: ExternalDataStripeSource) => ({ source }),
schemaLoadingFinished: (schema: ExternalDataSourceSchema) => ({ schema }),
abortAnyRunningQuery: true,
- setCurrentTab: (tab: DataWarehouseTab = DataWarehouseTab.ManagedSources) => ({ tab }),
deleteSelfManagedTable: (tableId: string) => ({ tableId }),
}),
loaders(({ cache, actions, values }) => ({
diff --git a/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx b/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx
deleted file mode 100644
index 3b72b10b129ec..0000000000000
--- a/frontend/src/scenes/data-warehouse/settings/source/DataWarehouseSourceSettingsScene.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { LemonButton, LemonTabs } from '@posthog/lemon-ui'
-import { useActions, useValues } from 'kea'
-import { PageHeader } from 'lib/components/PageHeader'
-import { SceneExport } from 'scenes/sceneTypes'
-import { urls } from 'scenes/urls'
-
-import { DataWarehouseSettingsTab } from '~/types'
-
-import {
- dataWarehouseSourceSettingsLogic,
- DataWarehouseSourceSettingsLogicProps,
- DataWarehouseSourceSettingsTabs,
-} from './dataWarehouseSourceSettingsLogic'
-import { Schemas } from './Schemas'
-import { Syncs } from './Syncs'
-
-const paramsToProps = ({
- params: { id, tab },
-}: {
- params: { id?: string; tab?: string }
-}): DataWarehouseSourceSettingsLogicProps => {
- if (!id || !tab) {
- throw new Error('Loaded DataWarehouseSourceSettings without eother `id` or `tab`')
- }
-
- return {
- id,
- parentSettingsTab: tab as DataWarehouseSettingsTab,
- }
-}
-
-export const scene: SceneExport = {
- component: DataWarehouseSourceSettingsScene,
- logic: dataWarehouseSourceSettingsLogic,
- paramsToProps,
-}
-
-const TabContent: Record JSX.Element> = {
- [DataWarehouseSourceSettingsTabs.Schemas]: Schemas,
- [DataWarehouseSourceSettingsTabs.Syncs]: Syncs,
-}
-
-const FriendlyTabNames: Record = {
- [DataWarehouseSourceSettingsTabs.Schemas]: 'Schemas',
- [DataWarehouseSourceSettingsTabs.Syncs]: 'Syncs',
-}
-
-export function DataWarehouseSourceSettingsScene(): JSX.Element {
- const { parentSettingsTab, currentTab } = useValues(dataWarehouseSourceSettingsLogic)
- const { setCurrentTab } = useActions(dataWarehouseSourceSettingsLogic)
-
- return (
-
-
- Return to sources
-
- }
- />
- setCurrentTab(tab as DataWarehouseSourceSettingsTabs)}
- tabs={Object.entries(TabContent).map(([tab, ContentComponent]) => ({
- label: FriendlyTabNames[tab as DataWarehouseSourceSettingsTabs],
- key: tab,
- content: ,
- }))}
- />
-
- )
-}
diff --git a/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx
index 916bc13c99f41..2a3fa469ac658 100644
--- a/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx
+++ b/frontend/src/scenes/data-warehouse/settings/source/Schemas.tsx
@@ -11,25 +11,29 @@ import {
Spinner,
Tooltip,
} from '@posthog/lemon-ui'
-import { useActions, useValues } from 'kea'
+import { BindLogic, useActions, useValues } from 'kea'
import { More } from 'lib/lemon-ui/LemonButton/More'
import { useEffect } from 'react'
import { defaultQuery } from 'scenes/data-warehouse/utils'
import { urls } from 'scenes/urls'
-import { DataWarehouseSyncInterval, DataWarehouseTab, ExternalDataSourceSchema } from '~/types'
+import { DataWarehouseSyncInterval, ExternalDataSourceSchema } from '~/types'
import { SyncMethodForm } from '../../external/forms/SyncMethodForm'
import { dataWarehouseSettingsLogic } from '../dataWarehouseSettingsLogic'
import { dataWarehouseSourcesTableSyncMethodModalLogic } from '../dataWarehouseSourcesTableSyncMethodModalLogic'
import { dataWarehouseSourceSettingsLogic } from './dataWarehouseSourceSettingsLogic'
-export const Schemas = (): JSX.Element => {
- const { source, sourceLoading } = useValues(dataWarehouseSourceSettingsLogic)
+interface SchemasProps {
+ id: string
+}
+
+export const Schemas = ({ id }: SchemasProps): JSX.Element => {
+ const { source, sourceLoading } = useValues(dataWarehouseSourceSettingsLogic({ id }))
return (
- <>
+
- >
+
)
}
@@ -150,7 +154,7 @@ export const SchemaTable = ({ schemas, isLoading }: SchemaTableProps): JSX.Eleme
if (schema.table) {
const query = defaultQuery(schema.table.name, schema.table.columns)
return (
-
+
{schema.table.name}
)
diff --git a/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx b/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx
index eb7b62fa84ddb..a86a41ec867ae 100644
--- a/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx
+++ b/frontend/src/scenes/data-warehouse/settings/source/Syncs.tsx
@@ -14,9 +14,13 @@ const StatusTagSetting: Record = {
Cancelled: 'default',
}
-export const Syncs = (): JSX.Element => {
- const { jobs, jobsLoading, canLoadMoreJobs } = useValues(dataWarehouseSourceSettingsLogic)
- const { loadMoreJobs } = useActions(dataWarehouseSourceSettingsLogic)
+interface SyncsProps {
+ id: string
+}
+
+export const Syncs = ({ id }: SyncsProps): JSX.Element => {
+ const { jobs, jobsLoading, canLoadMoreJobs } = useValues(dataWarehouseSourceSettingsLogic({ id }))
+ const { loadMoreJobs } = useActions(dataWarehouseSourceSettingsLogic({ id }))
return (
id),
actions({
- setCurrentTab: (tab: DataWarehouseSourceSettingsTabs) => ({ tab }),
- setParentSettingsTab: (tab: DataWarehouseTab) => ({ tab }),
setSourceId: (id: string) => ({ id }),
reloadSchema: (schema: ExternalDataSourceSchema) => ({ schema }),
resyncSchema: (schema: ExternalDataSourceSchema) => ({ schema }),
@@ -97,21 +79,9 @@ export const dataWarehouseSourceSettingsLogic = kea tab,
- },
- ],
- parentSettingsTab: [
- DataWarehouseTab.ManagedSources as DataWarehouseTab,
- {
- setParentSettingsTab: (_, { tab }) => tab,
- },
- ],
+ reducers(({ props }) => ({
sourceId: [
- '' as string,
+ props.id,
{
setSourceId: (_, { id }) => id,
},
@@ -123,29 +93,8 @@ export const dataWarehouseSourceSettingsLogic = kea true,
},
],
- }),
- selectors({
- breadcrumbs: [
- (s) => [s.parentSettingsTab, s.sourceId],
- (parentSettingsTab, sourceId): Breadcrumb[] => [
- {
- key: Scene.DataWarehouse,
- name: 'Data Warehouse',
- path: urls.dataWarehouse(),
- },
- {
- key: Scene.DataWarehouseSettings,
- name: 'Data Warehouse Settings',
- path: urls.dataWarehouse(parentSettingsTab),
- },
- {
- key: Scene.dataWarehouseSourceSettings,
- name: 'Data Warehouse Source Settings',
- path: urls.dataWarehouseSourceSettings(sourceId, parentSettingsTab),
- },
- ],
- ],
- }),
+ })),
+
listeners(({ values, actions, cache }) => ({
loadSourceSuccess: () => {
clearTimeout(cache.sourceRefreshTimeout)
@@ -218,17 +167,6 @@ export const dataWarehouseSourceSettingsLogic = kea ({
- '/data-warehouse/settings/:parentTab/:id': ({ parentTab, id }) => {
- if (id) {
- actions.setSourceId(id)
- }
-
- if (parentTab !== values.parentSettingsTab) {
- actions.setParentSettingsTab(parentTab as DataWarehouseTab)
- }
- },
- })),
afterMount(({ actions }) => {
actions.loadSource()
actions.loadJobs()
diff --git a/frontend/src/scenes/error-tracking/ErrorTrackingScene.tsx b/frontend/src/scenes/error-tracking/ErrorTrackingScene.tsx
index be73a8270b2ac..8809fd793264d 100644
--- a/frontend/src/scenes/error-tracking/ErrorTrackingScene.tsx
+++ b/frontend/src/scenes/error-tracking/ErrorTrackingScene.tsx
@@ -3,6 +3,7 @@ import { IconPerson } from '@posthog/icons'
import { LemonButton, LemonCheckbox, LemonDivider, LemonSegmentedButton, ProfilePicture } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { BindLogic, useActions, useValues } from 'kea'
+import { FeedbackNotice } from 'lib/components/FeedbackNotice'
import { MemberSelect } from 'lib/components/MemberSelect'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import { SceneExport } from 'scenes/sceneTypes'
@@ -48,6 +49,7 @@ export function ErrorTrackingScene(): JSX.Element {
return (
+
{selectedRows.length === 0 ? : }
diff --git a/frontend/src/scenes/experiments/Experiments.tsx b/frontend/src/scenes/experiments/Experiments.tsx
index 69a8556b4f34d..484e3bdf7cfd3 100644
--- a/frontend/src/scenes/experiments/Experiments.tsx
+++ b/frontend/src/scenes/experiments/Experiments.tsx
@@ -10,7 +10,7 @@ import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { More } from 'lib/lemon-ui/LemonButton/More'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
-import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
+import { atColumn, createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
import { LemonTableLink } from 'lib/lemon-ui/LemonTable/LemonTableLink'
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { Link } from 'lib/lemon-ui/Link'
@@ -64,13 +64,14 @@ export function Experiments(): JSX.Element {
},
createdByColumn() as LemonTableColumn,
createdAtColumn() as LemonTableColumn,
+ atColumn('start_date', 'Started') as LemonTableColumn,
{
title: 'Duration',
key: 'duration',
render: function Render(_, experiment: Experiment) {
const duration = getExperimentDuration(experiment)
- return {duration !== undefined ? `${duration} day${duration !== 1 ? 's' : ''}` : '--'}
+ return {duration !== undefined ? `${duration} day${duration !== 1 ? 's' : ''}` : '—'}
},
sorter: (a, b) => {
const durationA = getExperimentDuration(a) ?? -1
diff --git a/frontend/src/scenes/funnels/FunnelBarVertical/FunnelBarVertical.scss b/frontend/src/scenes/funnels/FunnelBarVertical/FunnelBarVertical.scss
index eb7a5c9666be3..8134c15ab25f9 100644
--- a/frontend/src/scenes/funnels/FunnelBarVertical/FunnelBarVertical.scss
+++ b/frontend/src/scenes/funnels/FunnelBarVertical/FunnelBarVertical.scss
@@ -171,6 +171,11 @@
.StepLegend {
height: 100%;
+
+ // overwrite antd-styles for legends without persons modal
+ // e.g. in exported insights
+ font-feature-settings: normal;
+ font-variant-numeric: normal;
white-space: nowrap;
border-left: 1px solid var(--border);
diff --git a/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx b/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx
index c21895d997a69..57d185d93866a 100644
--- a/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx
+++ b/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx
@@ -1,6 +1,8 @@
import { useActions, useValues } from 'kea'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { FEATURE_FLAGS } from 'lib/constants'
import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow'
import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic'
import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils'
@@ -23,19 +25,24 @@ export function FunnelsQuerySteps({ insightProps }: EditorFilterProps): JSX.Elem
const { series, querySource } = useValues(insightVizDataLogic(insightProps))
const { updateQuerySource } = useActions(insightVizDataLogic(insightProps))
- if (!isInsightQueryNode(querySource)) {
- return null
- }
+ const { featureFlags } = useValues(featureFlagLogic)
+ const mathAvailability = featureFlags[FEATURE_FLAGS.FIRST_TIME_FOR_USER_MATH]
+ ? MathAvailability.FunnelsOnly
+ : MathAvailability.None
- const actionFilters = queryNodeToFilter(querySource)
+ const actionFilters = isInsightQueryNode(querySource) ? queryNodeToFilter(querySource) : null
const setActionFilters = (payload: Partial): void => {
updateQuerySource({
- series: actionsAndEventsToSeries(payload as any, true, MathAvailability.None),
+ series: actionsAndEventsToSeries(payload as any, true, mathAvailability),
} as FunnelsQuery)
}
const { groupsTaxonomicTypes, showGroupsOptions } = useValues(groupsModel)
+ if (!actionFilters) {
+ return null
+ }
+
const filterSteps = series || []
const showSeriesIndicator = (series || []).length > 0
@@ -55,7 +62,7 @@ export function FunnelsQuerySteps({ insightProps }: EditorFilterProps): JSX.Elem
filters={actionFilters}
setFilters={setActionFilters}
typeKey={keyForInsightLogicProps('new')(insightProps)}
- mathAvailability={MathAvailability.None}
+ mathAvailability={mathAvailability}
hideDeleteBtn={filterSteps.length === 1}
buttonCopy="Add step"
showSeriesIndicator={showSeriesIndicator}
diff --git a/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx b/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx
index 763f817af4a43..11c2d5000f197 100644
--- a/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx
+++ b/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx
@@ -1,4 +1,5 @@
import { useActions, useValues } from 'kea'
+import { AlertDeletionWarning } from 'lib/components/Alerts/AlertDeletionWarning'
import { LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { Link } from 'lib/lemon-ui/Link'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
@@ -17,6 +18,7 @@ export function InsightsNav(): JSX.Element {
return (
<>
+
setActiveView(newKey)}
diff --git a/frontend/src/scenes/insights/InsightPageHeader.tsx b/frontend/src/scenes/insights/InsightPageHeader.tsx
index e0cf982908dbe..027b926e94498 100644
--- a/frontend/src/scenes/insights/InsightPageHeader.tsx
+++ b/frontend/src/scenes/insights/InsightPageHeader.tsx
@@ -88,8 +88,9 @@ export function InsightPageHeader({ insightLogicProps }: { insightLogicProps: In
canEditInsight={canEditInsight}
/>
push(urls.insightView(insight.short_id))}
+ isOpen={insightMode === ItemMode.Alerts}
+ insightLogicProps={insightLogicProps}
insightShortId={insight.short_id}
alertId={subscriptionId}
/>
@@ -241,7 +242,11 @@ export function InsightPageHeader({ insightLogicProps }: { insightLogicProps: In
{insightMode === ItemMode.Edit && hasDashboardItemId && (
- setInsightMode(ItemMode.View, null)}>
+ setInsightMode(ItemMode.View, null)}
+ data-attr="insight-cancel-edit-button"
+ >
Cancel
)}
diff --git a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx
index 32f7651006293..a1ecdd39c48c4 100644
--- a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx
+++ b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx
@@ -3,8 +3,16 @@ import './ActionFilterRow.scss'
import { DraggableSyntheticListeners } from '@dnd-kit/core'
import { useSortable } from '@dnd-kit/sortable'
import { CSS } from '@dnd-kit/utilities'
-import { IconCopy, IconFilter, IconPencil, IconTrash, IconWarning } from '@posthog/icons'
-import { LemonSelect, LemonSelectOption, LemonSelectOptions } from '@posthog/lemon-ui'
+import { IconCopy, IconEllipsis, IconFilter, IconPencil, IconTrash, IconWarning } from '@posthog/icons'
+import {
+ LemonBadge,
+ LemonCheckbox,
+ LemonDivider,
+ LemonMenu,
+ LemonSelect,
+ LemonSelectOption,
+ LemonSelectOptions,
+} from '@posthog/lemon-ui'
import { BuiltLogic, useActions, useValues } from 'kea'
import { EntityFilterInfo } from 'lib/components/EntityFilterInfo'
import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor'
@@ -61,6 +69,7 @@ const DragHandle = (props: DraggableSyntheticListeners | undefined): JSX.Element
export enum MathAvailability {
All,
ActorsOnly,
+ FunnelsOnly,
None,
}
@@ -160,6 +169,7 @@ export function ActionFilterRow({
const { dataWarehouseTablesMap } = useValues(databaseTableListLogic)
const [isHogQLDropdownVisible, setIsHogQLDropdownVisible] = useState(false)
+ const [isMenuVisible, setIsMenuVisible] = useState(false)
const { setNodeRef, attributes, transform, transition, listeners, isDragging } = useSortable({ id: filter.uuid })
@@ -176,19 +186,30 @@ export function ActionFilterRow({
const onClose = (): void => {
removeLocalFilter({ ...filter, index })
}
- const onMathSelect = (_: unknown, selectedMath: string): void => {
+ const onMathSelect = (_: unknown, selectedMath?: string): void => {
+ const mathProperties = selectedMath
+ ? {
+ ...mathTypeToApiValues(selectedMath),
+ math_property:
+ mathDefinitions[selectedMath]?.category === MathCategory.PropertyValue
+ ? mathProperty ?? '$time'
+ : undefined,
+ math_hogql:
+ mathDefinitions[selectedMath]?.category === MathCategory.HogQLExpression
+ ? mathHogQL ?? 'count()'
+ : undefined,
+ }
+ : {
+ math_property: undefined,
+ math_hogql: undefined,
+ math_group_type_index: undefined,
+ math: undefined,
+ }
+
updateFilterMath({
- ...mathTypeToApiValues(selectedMath),
- math_property:
- mathDefinitions[selectedMath]?.category === MathCategory.PropertyValue
- ? mathProperty ?? '$time'
- : undefined,
- math_hogql:
- mathDefinitions[selectedMath]?.category === MathCategory.HogQLExpression
- ? mathHogQL ?? 'count()'
- : undefined,
- type: filter.type,
index,
+ type: filter.type,
+ ...mathProperties,
})
}
const onMathPropertySelect = (_: unknown, property: string): void => {
@@ -284,18 +305,24 @@ export function ActionFilterRow({
)
+ const enablePopup = mathAvailability === MathAvailability.FunnelsOnly
+
const renameRowButton = (
}
title="Rename graph series"
data-attr={`show-prop-rename-${index}`}
- noPadding
+ noPadding={!enablePopup}
onClick={() => {
+ setIsMenuVisible(false)
selectFilter(filter)
onRenameClick()
}}
- />
+ fullWidth={enablePopup}
+ >
+ {enablePopup ? 'Rename' : undefined}
+
)
const duplicateRowButton = (
@@ -304,22 +331,32 @@ export function ActionFilterRow({
icon={}
title="Duplicate graph series"
data-attr={`show-prop-duplicate-${index}`}
- noPadding
+ noPadding={!enablePopup}
onClick={() => {
+ setIsMenuVisible(false)
duplicateFilter(filter)
}}
- />
+ fullWidth={enablePopup}
+ >
+ {enablePopup ? 'Duplicate' : undefined}
+
)
const deleteButton = (
}
- title="Delete graph series"
+ // title="Delete graph series"
data-attr={`delete-prop-filter-${index}`}
- noPadding
- onClick={onClose}
- />
+ noPadding={!enablePopup}
+ onClick={() => {
+ setIsMenuVisible(false)
+ onClose()
+ }}
+ fullWidth={enablePopup}
+ >
+ {enablePopup ? 'Delete' : undefined}
+
)
const rowStartElements = [
@@ -329,7 +366,7 @@ export function ActionFilterRow({
const rowEndElements = !readOnly
? [
- !hideFilter && propertyFiltersButton,
+ !hideFilter && !enablePopup && propertyFiltersButton,
!hideRename && renameRowButton,
!hideDuplicate && !singleFilter && duplicateRowButton,
!hideDeleteBtn && !singleFilter && deleteButton,
@@ -369,111 +406,180 @@ export function ActionFilterRow({
{filterElement}
{customRowSuffix !== undefined && <>{suffix}>}
- {mathAvailability !== MathAvailability.None && (
- <>
-
- {mathDefinitions[math || BaseMathType.TotalCount]?.category ===
- MathCategory.PropertyValue && (
-
-
onMathPropertySelect(index, currentValue)}
- eventNames={name ? [name] : []}
- data-attr="math-property-select"
- renderValue={(currentValue) => (
-
- Calculate{' '}
- {mathDefinitions[math ?? ''].name.toLowerCase()} of
- the session duration. This is based on the{' '}
- $session_id
property associated with
- events. The duration is derived from the time
- difference between the first and last event for each
- distinct $session_id
.
- >
- ) : (
- <>
- Calculate{' '}
- {mathDefinitions[math ?? ''].name.toLowerCase()}{' '}
- from property {currentValue}
. Note that
- only {name} occurences where{' '}
- {currentValue}
is set with a numeric
- value will be taken into account.
- >
- )
+ {mathAvailability !== MathAvailability.None &&
+ mathAvailability !== MathAvailability.FunnelsOnly && (
+ <>
+
+ {mathDefinitions[math || BaseMathType.TotalCount]?.category ===
+ MathCategory.PropertyValue && (
+
+
+ onMathPropertySelect(index, currentValue)
+ }
+ eventNames={name ? [name] : []}
+ data-attr="math-property-select"
+ renderValue={(currentValue) => (
+
+ Calculate{' '}
+ {mathDefinitions[math ?? ''].name.toLowerCase()}{' '}
+ of the session duration. This is based on the{' '}
+ $session_id
property associated
+ with events. The duration is derived from the
+ time difference between the first and last event
+ for each distinct $session_id
.
+ >
+ ) : (
+ <>
+ Calculate{' '}
+ {mathDefinitions[math ?? ''].name.toLowerCase()}{' '}
+ from property {currentValue}
. Note
+ that only {name} occurences where{' '}
+ {currentValue}
is set with a
+ numeric value will be taken into account.
+ >
+ )
+ }
+ placement="right"
+ >
+
+
+ )}
+ />
+
+ )}
+ {mathDefinitions[math || BaseMathType.TotalCount]?.category ===
+ MathCategory.HogQLExpression && (
+
+
setIsHogQLDropdownVisible(false)}
+ overlay={
+ // eslint-disable-next-line react/forbid-dom-props
+
+ {
+ onMathHogQLSelect(index, currentValue)
+ setIsHogQLDropdownVisible(false)
+ }}
+ />
+
+ }
+ >
+
+ setIsHogQLDropdownVisible(!isHogQLDropdownVisible)
}
- placement="right"
>
-
-
- )}
- />
-
- )}
- {mathDefinitions[math || BaseMathType.TotalCount]?.category ===
- MathCategory.HogQLExpression && (
-
- {mathHogQL}
+
+
+
+ )}
+ >
+ )}
+
+ {/* right section fixed */}
+ {rowEndElements.length ? (
+
+ {mathAvailability === MathAvailability.FunnelsOnly ? (
+ <>
+ {!hideFilter && propertyFiltersButton}
+
+
setIsHogQLDropdownVisible(false)}
- overlay={
- // eslint-disable-next-line react/forbid-dom-props
-
- {
- onMathHogQLSelect(index, currentValue)
- setIsHogQLDropdownVisible(false)
- }}
- />
-
- }
+ onVisibilityChange={setIsMenuVisible}
+ items={[
+ {
+ label: () => (
+ <>
+ {
+ onMathSelect(
+ index,
+ checked
+ ? BaseMathType.FirstTimeForUser
+ : undefined
+ )
+ }}
+ data-attr={`math-first-time-for-user-${index}`}
+ label="Count by first time for user"
+ fullWidth
+ />
+
+ >
+ ),
+ },
+ {
+ label: () => renameRowButton,
+ },
+ {
+ label: () => duplicateRowButton,
+ },
+ {
+ label: () => deleteButton,
+ },
+ ]}
>
setIsHogQLDropdownVisible(!isHogQLDropdownVisible)}
- >
- {mathHogQL}
-
-
+ size="medium"
+ aria-label="Show more actions"
+ data-attr={`more-button-${index}`}
+ icon={}
+ noPadding
+ />
+
+
- )}
- >
- )}
-
- {/* right section fixed */}
- {rowEndElements.length ?
{rowEndElements}
: null}
+ >
+ ) : (
+ rowEndElements
+ )}
+
+ ) : null}
>
)}