From ebddd25ba5b68541a7c2a2dd43812fd0eeaf6bb3 Mon Sep 17 00:00:00 2001 From: Paolo D'Amico Date: Wed, 12 May 2021 03:38:01 -0700 Subject: [PATCH] #4050 insights layouts & misc improvements (#4306) --- .../components/UnifiedPropertyFilter.tsx | 4 +- frontend/src/lib/components/icons.tsx | 4 +- frontend/src/lib/constants.tsx | 7 +- .../insights/ActionFilter/ActionFilter.tsx | 1 + .../ActionFilter/ActionFilterRow/index.scss | 17 ++ .../ActionFilter/ActionFilterRow/index.tsx | 74 ++++--- .../scenes/insights/ActionFilter/Sortable.tsx | 1 + .../InsightTabs/InsightDisplayConfig.tsx | 5 +- .../scenes/insights/InsightTabs/PathTab.tsx | 7 + .../InsightTabs/PathTabHorizontal.tsx | 82 ++++++++ .../insights/InsightTabs/RetentionTab.tsx | 7 + .../InsightTabs/RetentionTabHorizontal.tsx | 187 ++++++++++++++++++ .../insights/InsightTabs/SessionTab.tsx | 7 + .../InsightTabs/SessionTabHorizontal.tsx | 54 +++++ frontend/src/scenes/insights/Insights.scss | 8 + .../scenes/insights/RetentionDatePicker.tsx | 36 ++++ frontend/src/scenes/paths/pathsLogic.ts | 13 +- .../scenes/retention/retentionTableLogic.ts | 18 +- frontend/src/scenes/trends/trendsLogic.ts | 2 +- frontend/src/types.ts | 5 +- 20 files changed, 488 insertions(+), 51 deletions(-) create mode 100644 frontend/src/scenes/insights/InsightTabs/PathTabHorizontal.tsx create mode 100644 frontend/src/scenes/insights/InsightTabs/RetentionTabHorizontal.tsx create mode 100644 frontend/src/scenes/insights/InsightTabs/SessionTabHorizontal.tsx create mode 100644 frontend/src/scenes/insights/RetentionDatePicker.tsx diff --git a/frontend/src/lib/components/PropertyFilters/components/UnifiedPropertyFilter.tsx b/frontend/src/lib/components/PropertyFilters/components/UnifiedPropertyFilter.tsx index b97e503ee45..f6a0eab3bec 100644 --- a/frontend/src/lib/components/PropertyFilters/components/UnifiedPropertyFilter.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/UnifiedPropertyFilter.tsx @@ -224,10 +224,10 @@ export function UnifiedPropertyFilter({ index, onComplete, logic }: PropertyFilt onClick={onClick} style={{ display: 'flex', alignItems: 'center' }} ref={selectBoxToggleRef} + icon={!key ? : null} > - {!key && } - + {key ? : 'Add filter'} {key && } diff --git a/frontend/src/lib/components/icons.tsx b/frontend/src/lib/components/icons.tsx index ed2ec44f989..3af0846e36e 100644 --- a/frontend/src/lib/components/icons.tsx +++ b/frontend/src/lib/components/icons.tsx @@ -256,9 +256,9 @@ export function IconToolbar(): JSX.Element { ) } -export function IconExternalLink(): JSX.Element { +export function IconExternalLink({ style }: { style?: CSSProperties }): JSX.Element { return ( - + ([ export const PERSON_DISTINCT_ID_MAX_SIZE = 3 +// Event constants export const PAGEVIEW = '$pageview' export const AUTOCAPTURE = '$autocapture' export const SCREEN = '$screen' @@ -58,9 +59,13 @@ export enum ShownAsValue { LIFECYCLE = 'Lifecycle', } +// Retention constants +export const RETENTION_RECURRING = 'retention_recurring' +export const RETENTION_FIRST_TIME = 'retention_first_time' + +// Properties constants export const PROPERTY_MATH_TYPE = 'property' export const EVENT_MATH_TYPE = 'event' - export const MATHS: Record = { total: { name: 'Total volume', diff --git a/frontend/src/scenes/insights/ActionFilter/ActionFilter.tsx b/frontend/src/scenes/insights/ActionFilter/ActionFilter.tsx index c0d832a07c2..4800e2911c8 100644 --- a/frontend/src/scenes/insights/ActionFilter/ActionFilter.tsx +++ b/frontend/src/scenes/insights/ActionFilter/ActionFilter.tsx @@ -93,6 +93,7 @@ export function ActionFilter({ singleFilter={singleFilter} showOr={showOr} horizontalUI={horizontalUI} + filterCount={localFilters.length} /> )) ) diff --git a/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.scss b/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.scss index 779239bb0a1..4f831e78e48 100644 --- a/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.scss +++ b/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.scss @@ -69,6 +69,12 @@ &.visible { background-color: $primary; color: #fff; + + &:hover { + background-color: #fff; + color: $primary; + border: 1px solid $primary; + } } } } @@ -80,6 +86,17 @@ background-color: #f6f6f6; } } + +.horizontal-ui { + .row-action-btn { + &.delete { + padding: 0; + width: unset; + color: rgba($danger, 0.6); + } + } +} + .action-row-letter { line-height: 32px; padding-right: 1px; diff --git a/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.tsx b/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.tsx index a9c9106f93f..d880cf0811a 100644 --- a/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.tsx +++ b/frontend/src/scenes/insights/ActionFilter/ActionFilterRow/index.tsx @@ -5,7 +5,7 @@ import { ActionFilter, EntityTypes, PropertyFilter, SelectOption } from '~/types import { ActionFilterDropdown } from './ActionFilterDropdown' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' import { PROPERTY_MATH_TYPE, EVENT_MATH_TYPE, MATHS } from 'lib/constants' -import { DownOutlined, DeleteOutlined, FilterOutlined } from '@ant-design/icons' +import { DownOutlined, DeleteOutlined, FilterOutlined, CloseSquareOutlined } from '@ant-design/icons' import { SelectGradientOverflow } from 'lib/components/SelectGradientOverflow' import { BareEntity, entityFilterLogic } from '../entityFilterLogic' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' @@ -38,6 +38,7 @@ interface ActionFilterRowProps { showOr?: boolean letter?: string | null horizontalUI?: boolean + filterCount: number } export function ActionFilterRow({ @@ -50,6 +51,7 @@ export function ActionFilterRow({ showOr, letter, horizontalUI = false, + filterCount, }: ActionFilterRowProps): JSX.Element { const node = useRef(null) const { selectedFilter, entities, entityFilterVisible } = useValues(logic) @@ -109,53 +111,60 @@ export function ActionFilterRow({ value = entity.id || filter.id } + const orLabel =
OR
+ return (
- {showOr && ( - - {index > 0 && ( -
- OR -
- )} + {!horizontalUI && index > 0 && showOr && ( + + {orLabel} )} + + {horizontalUI && !singleFilter && filterCount > 1 && ( + + )} - {!singleFilter && ( + {!horizontalUI && !singleFilter && filterCount > 1 && (
) diff --git a/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx b/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx index 6428adbe59e..cf2b9b4d722 100644 --- a/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx +++ b/frontend/src/scenes/insights/InsightTabs/InsightDisplayConfig.tsx @@ -11,6 +11,7 @@ import { DisplayType, FilterType } from '~/types' import { ViewType } from '../insightLogic' import { CalendarOutlined } from '@ant-design/icons' import { InsightDateFilter } from '../InsightDateFilter' +import { RetentionDatePicker } from '../RetentionDatePicker' interface InsightDisplayConfigProps { clearAnnotationsToCreate: () => void @@ -157,8 +158,10 @@ function HorizontalDefaultInsightDisplayConfig({ disabled={allFilters.insight === ViewType.LIFECYCLE} /> )} - {showIntervalFilter(activeView, allFilters) && } + + {activeView === ViewType.RETENTION && } + {showDateFilter[activeView] && ( <> : +} + +function DefaultPathTab(): JSX.Element { const { customEventNames } = useValues(eventDefinitionsLogic) const { filter } = useValues(pathsLogic({ dashboardItemId: null })) const { setFilter } = useActions(pathsLogic({ dashboardItemId: null })) diff --git a/frontend/src/scenes/insights/InsightTabs/PathTabHorizontal.tsx b/frontend/src/scenes/insights/InsightTabs/PathTabHorizontal.tsx new file mode 100644 index 00000000000..2383538d83c --- /dev/null +++ b/frontend/src/scenes/insights/InsightTabs/PathTabHorizontal.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import { useValues, useActions } from 'kea' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { + PAGEVIEW, + AUTOCAPTURE, + CUSTOM_EVENT, + pathOptionsToLabels, + pathOptionsToProperty, + pathsLogic, +} from 'scenes/paths/pathsLogic' +import { Col, Row, Select, Skeleton } from 'antd' +import { PropertyValue } from 'lib/components/PropertyFilters' +import { TestAccountFilter } from '../TestAccountFilter' +import { eventDefinitionsLogic } from 'scenes/events/eventDefinitionsLogic' +import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint' + +export function PathTabHorizontal(): JSX.Element { + const { customEventNames } = useValues(eventDefinitionsLogic) + const { filter, filtersLoading } = useValues(pathsLogic({ dashboardItemId: null })) + const { setFilter } = useActions(pathsLogic({ dashboardItemId: null })) + + const screens = useBreakpoint() + const isSmallScreen = screens.xs || (screens.sm && !screens.md) + + return ( + + + + Showing paths from + + + + starting at + + ({ + name, + })) + } + onSet={(value: string | number): void => setFilter({ start_point: value })} + propertyKey={pathOptionsToProperty[filter.path_type || PAGEVIEW]} + type="event" + style={{ width: 200, paddingTop: 2 }} + value={filter.start_point} + placeholder={'Select start element'} + operator={null} + /> + + + + +

Global Filters

+ {filtersLoading ? ( + + ) : ( + <> + + + + )} + +
+ ) +} diff --git a/frontend/src/scenes/insights/InsightTabs/RetentionTab.tsx b/frontend/src/scenes/insights/InsightTabs/RetentionTab.tsx index d00dfe6d93a..80a11f5283a 100644 --- a/frontend/src/scenes/insights/InsightTabs/RetentionTab.tsx +++ b/frontend/src/scenes/insights/InsightTabs/RetentionTab.tsx @@ -21,10 +21,17 @@ import { FilterType } from '~/types' import { TestAccountFilter } from '../TestAccountFilter' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import './RetentionTab.scss' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { RetentionTabHorizontal } from './RetentionTabHorizontal' const DatePicker = generatePicker(dayjsGenerateConfig) export function RetentionTab(): JSX.Element { + const { featureFlags } = useValues(featureFlagLogic) + return featureFlags['4050-query-ui-optB'] ? : +} + +function DefaultRetentionTab(): JSX.Element { const node = useRef(null) const returningNode = useRef(null) const [open, setOpen] = useState(false) diff --git a/frontend/src/scenes/insights/InsightTabs/RetentionTabHorizontal.tsx b/frontend/src/scenes/insights/InsightTabs/RetentionTabHorizontal.tsx new file mode 100644 index 00000000000..0f426fbbd42 --- /dev/null +++ b/frontend/src/scenes/insights/InsightTabs/RetentionTabHorizontal.tsx @@ -0,0 +1,187 @@ +import React, { useState, useRef } from 'react' +import { useValues, useActions } from 'kea' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { ActionFilterDropdown } from '../ActionFilter/ActionFilterRow/ActionFilterDropdown' +import { entityFilterLogic } from '../ActionFilter/entityFilterLogic' + +import { DownOutlined, InfoCircleOutlined } from '@ant-design/icons' +import { retentionTableLogic, dateOptions, retentionOptionDescriptions } from 'scenes/retention/retentionTableLogic' +import { Button, Select, Tooltip, Row, Col, Skeleton } from 'antd' + +import { FilterType, RetentionType } from '~/types' +import { TestAccountFilter } from '../TestAccountFilter' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import './RetentionTab.scss' +import { RETENTION_FIRST_TIME, RETENTION_RECURRING } from 'lib/constants' +import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint' +import { IconExternalLink } from 'lib/components/icons' + +export function RetentionTabHorizontal(): JSX.Element { + const node = useRef(null) + const returningNode = useRef(null) + const [open, setOpen] = useState(false) + const [returningOpen, setReturningOpen] = useState(false) + const { filters, actionsLookup, filtersLoading } = useValues(retentionTableLogic({ dashboardItemId: null })) + const { setFilters } = useActions(retentionTableLogic({ dashboardItemId: null })) + + const screens = useBreakpoint() + const isSmallScreen = screens.xs || (screens.sm && !screens.md) + + const entityLogic = entityFilterLogic({ + setFilters: (filters: FilterType) => { + if (filters.events && filters.events.length > 0) { + setFilters({ target_entity: filters.events[0] }) + } else if (filters.actions && filters.actions.length > 0) { + setFilters({ target_entity: filters.actions[0] }) + } else { + setFilters({ target_entity: null }) + } + setOpen(false) + }, + filters: filters.target_entity, + typeKey: 'retention-table', + singleMode: true, + }) + + const entityLogicReturning = entityFilterLogic({ + setFilters: (filters: FilterType) => { + if (filters.events && filters.events.length > 0) { + setFilters({ returning_entity: filters.events[0] }) + } else if (filters.actions && filters.actions.length > 0) { + setFilters({ returning_entity: filters.actions[0] }) + } else { + setFilters({ returning_entity: null }) + } + setReturningOpen(false) + }, + filters: filters.returning_entity, + typeKey: 'retention-table-returning', + singleMode: true, + }) + + const selectedRetainingEvent = + filters.returning_entity?.name || + (filters.returning_entity.id && actionsLookup[filters.returning_entity.id]) || + 'Select action' + + const selectedCohortizingEvent = + filters.target_entity?.name || + (filters.target_entity.id && actionsLookup[filters.target_entity.id]) || + 'Select action' + + // TODO: Update constant in retentionTableLogic.ts when releasing 4050 + const retentionOptions = { + [`${RETENTION_FIRST_TIME}`]: 'for the first time', + [`${RETENTION_RECURRING}`]: 'recurringly', + } + + return ( +
+ + + + + Showing Unique users who did + + + + setOpen(false)} + /> + + +
+ +
+ + grouped by + + + +
+ + ... who then came back and did + + + setReturningOpen(false)} + /> + + + + +

+ Want to learn more about retention?{' '} + + Go to docs + + +

+ +
+ + +

Global Filters

+ {filtersLoading ? ( + + ) : ( + <> + + + + )} + +
+
+ ) +} diff --git a/frontend/src/scenes/insights/InsightTabs/SessionTab.tsx b/frontend/src/scenes/insights/InsightTabs/SessionTab.tsx index c321a78e76b..81b0d31f7d6 100644 --- a/frontend/src/scenes/insights/InsightTabs/SessionTab.tsx +++ b/frontend/src/scenes/insights/InsightTabs/SessionTab.tsx @@ -9,8 +9,15 @@ import { FilterType } from '~/types' import { Tooltip } from 'antd' import { InfoCircleOutlined } from '@ant-design/icons' import { TestAccountFilter } from '../TestAccountFilter' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { SessionTabHorizontal } from './SessionTabHorizontal' export function SessionTab(): JSX.Element { + const { featureFlags } = useValues(featureFlagLogic) + return featureFlags['4050-query-ui-optB'] ? : +} + +function DefaultSessionTab(): JSX.Element { const { filters } = useValues(trendsLogic({ dashboardItemId: null, view: ViewType.SESSIONS })) const { setFilters } = useActions(trendsLogic({ dashboardItemId: null, view: ViewType.SESSIONS })) diff --git a/frontend/src/scenes/insights/InsightTabs/SessionTabHorizontal.tsx b/frontend/src/scenes/insights/InsightTabs/SessionTabHorizontal.tsx new file mode 100644 index 00000000000..bb4c372d61a --- /dev/null +++ b/frontend/src/scenes/insights/InsightTabs/SessionTabHorizontal.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import { useValues, useActions } from 'kea' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { SessionFilter } from 'lib/components/SessionsFilter' +import { ViewType } from '../insightLogic' +import { trendsLogic } from '../../trends/trendsLogic' +import { ActionFilter } from '../ActionFilter/ActionFilter' +import { FilterType } from '~/types' +import { Col, Row, Skeleton } from 'antd' +import { TestAccountFilter } from '../TestAccountFilter' +import useBreakpoint from 'antd/lib/grid/hooks/useBreakpoint' + +export function SessionTabHorizontal(): JSX.Element { + const { filters, filtersLoading } = useValues(trendsLogic({ dashboardItemId: null, view: ViewType.SESSIONS })) + const { setFilters } = useActions(trendsLogic({ dashboardItemId: null, view: ViewType.SESSIONS })) + + const screens = useBreakpoint() + const isSmallScreen = screens.xs || (screens.sm && !screens.md) + + return ( + + + + Showing + + setFilters({ session: v })} /> + + where a user did any of the following: + + + ) => setFilters(payload)} + typeKey={'sessions' + ViewType.SESSIONS} + hideMathSelector={true} + buttonCopy="Add action or event" + showOr={true} + horizontalUI + /> + + +

Global Filters

+ {filtersLoading ? ( + + ) : ( + <> + + + + )} + +
+ ) +} diff --git a/frontend/src/scenes/insights/Insights.scss b/frontend/src/scenes/insights/Insights.scss index 111fabf2948..4f51f794836 100644 --- a/frontend/src/scenes/insights/Insights.scss +++ b/frontend/src/scenes/insights/Insights.scss @@ -78,6 +78,14 @@ } } } + + .retention-date-picker { + background-color: transparent; + border: 0; + input::placeholder { + color: $text_default; + } + } } .insight-empty-state { diff --git a/frontend/src/scenes/insights/RetentionDatePicker.tsx b/frontend/src/scenes/insights/RetentionDatePicker.tsx new file mode 100644 index 00000000000..47203884e1f --- /dev/null +++ b/frontend/src/scenes/insights/RetentionDatePicker.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import dayjs from 'dayjs' +import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs' +import generatePicker from 'antd/es/date-picker/generatePicker' +import { useActions, useValues } from 'kea' +import { retentionTableLogic } from 'scenes/retention/retentionTableLogic' +import { CalendarOutlined } from '@ant-design/icons' +import { Tooltip } from 'antd' + +const DatePicker = generatePicker(dayjsGenerateConfig) + +export function RetentionDatePicker(): JSX.Element { + const { filters } = useValues(retentionTableLogic({ dashboardItemId: null })) + const { setFilters } = useActions(retentionTableLogic({ dashboardItemId: null })) + const yearSuffix = filters.date_to && dayjs(filters.date_to).year() !== dayjs().year() ? ', YYYY' : '' + return ( + <> + + + + setFilters({ date_to: date_to && dayjs(date_to).toISOString() })} + allowClear + placeholder="Today" + className="retention-date-picker" + suffixIcon={null} + /> + + + + ) +} diff --git a/frontend/src/scenes/paths/pathsLogic.ts b/frontend/src/scenes/paths/pathsLogic.ts index b4f9afffb99..c8b9ce3963f 100644 --- a/frontend/src/scenes/paths/pathsLogic.ts +++ b/frontend/src/scenes/paths/pathsLogic.ts @@ -7,6 +7,7 @@ import { insightHistoryLogic } from 'scenes/insights/InsightHistoryPanel/insight import { pathsLogicType } from './pathsLogicType' import { FilterType, PropertyFilter } from '~/types' import { dashboardItemsModel } from '~/models/dashboardItemsModel' +import { propertyDefinitionsLogic } from 'scenes/events/propertyDefinitionsLogic' export const PAGEVIEW = '$pageview' export const SCREEN = '$screen' @@ -14,10 +15,10 @@ export const AUTOCAPTURE = '$autocapture' export const CUSTOM_EVENT = 'custom_event' export const pathOptionsToLabels = { - [`${PAGEVIEW}`]: 'Pageview (Web)', - [`${SCREEN}`]: 'Screen (Mobile)', - [`${AUTOCAPTURE}`]: 'Autocaptured Events', - [`${CUSTOM_EVENT}`]: 'Custom Events', + [`${PAGEVIEW}`]: 'Page views (Web)', + [`${SCREEN}`]: 'Screen views (Mobile)', + [`${AUTOCAPTURE}`]: 'Autocaptured events', + [`${CUSTOM_EVENT}`]: 'Custom events', } export const pathOptionsToProperty = { @@ -177,6 +178,10 @@ export const pathsLogic = kea [propertyDefinitionsLogic.selectors.loaded], + (propertiesLoaded): boolean => !propertiesLoaded, + ], }, actionToUrl: ({ values }) => ({ setProperties: () => { diff --git a/frontend/src/scenes/retention/retentionTableLogic.ts b/frontend/src/scenes/retention/retentionTableLogic.ts index d21f3b9f7d5..580f2e7d174 100644 --- a/frontend/src/scenes/retention/retentionTableLogic.ts +++ b/frontend/src/scenes/retention/retentionTableLogic.ts @@ -5,9 +5,9 @@ import { toParams, objectsEqual } from 'lib/utils' import { ViewType, insightLogic } from 'scenes/insights/insightLogic' import { insightHistoryLogic } from 'scenes/insights/InsightHistoryPanel/insightHistoryLogic' import { retentionTableLogicType } from './retentionTableLogicType' -import { ACTIONS_LINE_GRAPH_LINEAR, ACTIONS_TABLE } from 'lib/constants' +import { ACTIONS_LINE_GRAPH_LINEAR, ACTIONS_TABLE, RETENTION_FIRST_TIME, RETENTION_RECURRING } from 'lib/constants' import { actionsModel } from '~/models' -import { ActionType } from '~/types' +import { ActionType, FilterType } from '~/types' import { RetentionTablePayload, RetentionTrendPayload, @@ -15,12 +15,11 @@ import { RetentionTrendPeoplePayload, } from 'scenes/retention/types' import { dashboardItemsModel } from '~/models/dashboardItemsModel' +import { eventDefinitionsLogic } from 'scenes/events/eventDefinitionsLogic' +import { propertyDefinitionsLogic } from 'scenes/events/propertyDefinitionsLogic' export const dateOptions = ['Hour', 'Day', 'Week', 'Month'] -const RETENTION_RECURRING = 'retention_recurring' -const RETENTION_FIRST_TIME = 'retention_first_time' - export const retentionOptions = { [`${RETENTION_FIRST_TIME}`]: 'First Time', [`${RETENTION_RECURRING}`]: 'Recurring', @@ -103,6 +102,7 @@ export const retentionTableLogic = kea< values: [actionsModel, ['actions']], }, actions: () => ({ + // TODO: This needs to be properly typed with `FilterType`. N.B. We're currently mixing snake_case and pascalCase attribute names. setFilters: (filters: Record) => ({ filters }), loadMorePeople: true, updatePeople: (people) => ({ people }), @@ -114,7 +114,7 @@ export const retentionTableLogic = kea< filters: [ props.filters ? defaultFilters(props.filters as Record) - : (state: Record) => defaultFilters(router.selectors.searchParams(state)), + : (state) => defaultFilters(router.selectors.searchParams(state)), { setFilters: (state, { filters }) => ({ ...state, ...filters }), }, @@ -140,6 +140,10 @@ export const retentionTableLogic = kea< (selectors) => [(selectors as any).actions], (actions: ActionType[]) => Object.assign({}, ...actions.map((action) => ({ [action.id]: action.name }))), ], + filtersLoading: [ + () => [eventDefinitionsLogic.selectors.loaded, propertyDefinitionsLogic.selectors.loaded], + (eventsLoaded, propertiesLoaded) => !eventsLoaded || !propertiesLoaded, + ], }, events: ({ actions, props }) => ({ afterMount: () => props.dashboardItemId && actions.loadResults(), @@ -202,7 +206,7 @@ export const retentionTableLogic = kea< actions.updatePeople(newPeople) } }, - [dashboardItemsModel.actionTypes.refreshAllDashboardItems]: (filters: Record) => { + [dashboardItemsModel.actionTypes.refreshAllDashboardItems]: (filters: FilterType) => { if (props.dashboardItemId) { actions.setFilters(filters) } diff --git a/frontend/src/scenes/trends/trendsLogic.ts b/frontend/src/scenes/trends/trendsLogic.ts index 04a2a9dc932..edc988ef4bd 100644 --- a/frontend/src/scenes/trends/trendsLogic.ts +++ b/frontend/src/scenes/trends/trendsLogic.ts @@ -304,7 +304,7 @@ export const trendsLogic = kea< selectors: () => ({ filtersLoading: [ () => [eventDefinitionsLogic.selectors.loaded, propertyDefinitionsLogic.selectors.loaded], - (eventsLoaded, propertiesLoaded) => !eventsLoaded || !propertiesLoaded, + (eventsLoaded, propertiesLoaded): boolean => !eventsLoaded || !propertiesLoaded, ], results: [(selectors) => [selectors._results], (response) => response.result], resultsLoading: [(selectors) => [selectors._resultsLoading], (_resultsLoading) => _resultsLoading], diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 1fbebd9187a..1d8c2b98c91 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -8,6 +8,8 @@ import { PAGEVIEW, SCREEN, ShownAsValue, + RETENTION_RECURRING, + RETENTION_FIRST_TIME, } from 'lib/constants' import { PluginConfigSchema } from '@posthog/plugin-scaffold' import { PluginInstallationType } from 'scenes/plugins/types' @@ -507,7 +509,8 @@ export type InsightType = 'TRENDS' | 'SESSIONS' | 'FUNNELS' | 'RETENTION' | 'PAT export type ShownAsType = ShownAsValue // DEPRECATED: Remove when releasing `remove-shownas` export type BreakdownType = 'cohort' | 'person' | 'event' export type PathType = typeof PAGEVIEW | typeof AUTOCAPTURE | typeof SCREEN | typeof CUSTOM_EVENT -export type RetentionType = 'retention_recurring' | 'retention_first_time' + +export type RetentionType = typeof RETENTION_RECURRING | typeof RETENTION_FIRST_TIME export interface FilterType { insight?: InsightType