{!isEditingBillingLimit ? (
@@ -104,6 +108,7 @@ export const BillingLimitInput = ({ product }: { product: BillingProductV2Type }
<>
{
const { surveyID, surveyResponse } = useValues(billingProductLogic({ product }))
diff --git a/frontend/src/scenes/billing/billing-utils.spec.ts b/frontend/src/scenes/billing/billing-utils.spec.ts
index a28188ba901a3..48c34f4da53ba 100644
--- a/frontend/src/scenes/billing/billing-utils.spec.ts
+++ b/frontend/src/scenes/billing/billing-utils.spec.ts
@@ -1,3 +1,9 @@
+import { dayjs } from 'lib/dayjs'
+import tk from 'timekeeper'
+
+import billingJson from '~/mocks/fixtures/_billing_v2.json'
+import billingJsonWithFlatFee from '~/mocks/fixtures/_billing_v2_with_flat_fee.json'
+
import {
convertAmountToUsage,
convertLargeNumberToWords,
@@ -5,10 +11,6 @@ import {
projectUsage,
summarizeUsage,
} from './billing-utils'
-import tk from 'timekeeper'
-import { dayjs } from 'lib/dayjs'
-import billingJson from '~/mocks/fixtures/_billing_v2.json'
-import billingJsonWithFlatFee from '~/mocks/fixtures/_billing_v2_with_flat_fee.json'
describe('summarizeUsage', () => {
it('should summarise usage', () => {
diff --git a/frontend/src/scenes/billing/billing-utils.ts b/frontend/src/scenes/billing/billing-utils.ts
index 4b55f893bf04e..b6b152c099fe4 100644
--- a/frontend/src/scenes/billing/billing-utils.ts
+++ b/frontend/src/scenes/billing/billing-utils.ts
@@ -1,4 +1,5 @@
import { dayjs } from 'lib/dayjs'
+
import { BillingProductV2Type, BillingV2TierType, BillingV2Type } from '~/types'
export const summarizeUsage = (usage: number | null): string => {
diff --git a/frontend/src/scenes/billing/billingLogic.ts b/frontend/src/scenes/billing/billingLogic.ts
index 60aa5bf9dc27b..63c5c5222d9e8 100644
--- a/frontend/src/scenes/billing/billingLogic.ts
+++ b/frontend/src/scenes/billing/billingLogic.ts
@@ -1,18 +1,21 @@
-import { kea, path, actions, connect, afterMount, selectors, listeners, reducers } from 'kea'
+import { lemonToast } from '@posthog/lemon-ui'
+import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea'
+import { forms } from 'kea-forms'
import { loaders } from 'kea-loaders'
-import api from 'lib/api'
-import { BillingProductV2Type, BillingV2Type } from '~/types'
import { router, urlToAction } from 'kea-router'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import api from 'lib/api'
import { dayjs } from 'lib/dayjs'
-import { lemonToast } from '@posthog/lemon-ui'
+import { LemonBannerAction } from 'lib/lemon-ui/LemonBanner/LemonBanner'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { pluralize } from 'lib/utils'
+import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import posthog from 'posthog-js'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { userLogic } from 'scenes/userLogic'
-import { pluralize } from 'lib/utils'
+
+import { BillingProductV2Type, BillingV2Type, ProductKey } from '~/types'
+
import type { billingLogicType } from './billingLogicType'
-import { forms } from 'kea-forms'
-import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
export const ALLOCATION_THRESHOLD_ALERT = 0.85 // Threshold to show warning of event usage near limit
export const ALLOCATION_THRESHOLD_BLOCK = 1.2 // Threshold to block usage
@@ -24,6 +27,8 @@ export interface BillingAlertConfig {
contactSupport?: boolean
buttonCTA?: string
dismissKey?: string
+ action?: LemonBannerAction
+ pathName?: string
}
const parseBillingResponse = (data: Partial): BillingV2Type => {
@@ -53,6 +58,8 @@ const parseBillingResponse = (data: Partial): BillingV2Type => {
export const billingLogic = kea([
path(['scenes', 'billing', 'billingLogic']),
actions({
+ setProductSpecificAlert: (productSpecificAlert: BillingAlertConfig | null) => ({ productSpecificAlert }),
+ setScrollToProductKey: (scrollToProductKey: ProductKey | null) => ({ scrollToProductKey }),
setShowLicenseDirectInput: (show: boolean) => ({ show }),
reportBillingAlertShown: (alertConfig: BillingAlertConfig) => ({ alertConfig }),
reportBillingAlertActionClicked: (alertConfig: BillingAlertConfig) => ({ alertConfig }),
@@ -66,6 +73,18 @@ export const billingLogic = kea([
actions: [userLogic, ['loadUser'], eventUsageLogic, ['reportProductUnsubscribed']],
}),
reducers({
+ scrollToProductKey: [
+ null as ProductKey | null,
+ {
+ setScrollToProductKey: (_, { scrollToProductKey }) => scrollToProductKey,
+ },
+ ],
+ productSpecificAlert: [
+ null as BillingAlertConfig | null,
+ {
+ setProductSpecificAlert: (_, { productSpecificAlert }) => productSpecificAlert,
+ },
+ ],
showLicenseDirectInput: [
false,
{
@@ -144,8 +163,12 @@ export const billingLogic = kea([
},
],
billingAlert: [
- (s) => [s.billing, s.preflight, s.projectedTotalAmountUsd],
- (billing, preflight, projectedTotalAmountUsd): BillingAlertConfig | undefined => {
+ (s) => [s.billing, s.preflight, s.projectedTotalAmountUsd, s.productSpecificAlert],
+ (billing, preflight, projectedTotalAmountUsd, productSpecificAlert): BillingAlertConfig | undefined => {
+ if (productSpecificAlert) {
+ return productSpecificAlert
+ }
+
if (!billing || !preflight?.cloud) {
return
}
@@ -320,6 +343,10 @@ export const billingLogic = kea([
actions.setActivateLicenseValues({ license: hash.license })
actions.submitActivateLicense()
}
+ if (_search.products) {
+ const products = _search.products.split(',')
+ actions.setScrollToProductKey(products[0])
+ }
actions.setRedirectPath()
actions.setIsOnboarding()
},
diff --git a/frontend/src/scenes/billing/billingProductLogic.ts b/frontend/src/scenes/billing/billingProductLogic.ts
index aeb72f177c5be..5d78ef5ac7e81 100644
--- a/frontend/src/scenes/billing/billingProductLogic.ts
+++ b/frontend/src/scenes/billing/billingProductLogic.ts
@@ -1,21 +1,36 @@
-import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
+import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea'
+import posthog from 'posthog-js'
+import React from 'react'
+
import { BillingProductV2AddonType, BillingProductV2Type, BillingV2PlanType, BillingV2TierType } from '~/types'
+
+import { convertAmountToUsage } from './billing-utils'
import { billingLogic } from './billingLogic'
import type { billingProductLogicType } from './billingProductLogicType'
-import { convertAmountToUsage } from './billing-utils'
-import posthog from 'posthog-js'
const DEFAULT_BILLING_LIMIT = 500
+export interface BillingProductLogicProps {
+ product: BillingProductV2Type | BillingProductV2AddonType
+ billingLimitInputRef?: React.MutableRefObject
+}
+
export const billingProductLogic = kea([
+ props({} as BillingProductLogicProps),
key((props) => props.product.type),
path(['scenes', 'billing', 'billingProductLogic']),
connect({
- values: [billingLogic, ['billing', 'isUnlicensedDebug']],
- actions: [billingLogic, ['loadBillingSuccess', 'updateBillingLimitsSuccess', 'deactivateProduct']],
- }),
- props({
- product: {} as BillingProductV2Type | BillingProductV2AddonType,
+ values: [billingLogic, ['billing', 'isUnlicensedDebug', 'scrollToProductKey']],
+ actions: [
+ billingLogic,
+ [
+ 'loadBillingSuccess',
+ 'updateBillingLimitsSuccess',
+ 'deactivateProduct',
+ 'setProductSpecificAlert',
+ 'setScrollToProductKey',
+ ],
+ ],
}),
actions({
setIsEditingBillingLimit: (isEditingBillingLimit: boolean) => ({ isEditingBillingLimit }),
@@ -215,5 +230,40 @@ export const billingProductLogic = kea([
})
actions.setSurveyID('')
},
+ setScrollToProductKey: ({ scrollToProductKey }) => {
+ if (scrollToProductKey && scrollToProductKey === props.product.type) {
+ const { currentPlan } = values.currentAndUpgradePlans
+
+ if (currentPlan.initial_billing_limit) {
+ actions.setProductSpecificAlert({
+ status: 'warning',
+ title: 'Billing Limit Automatically Applied',
+ pathName: '/organization/billing',
+ dismissKey: `auto-apply-billing-limit-${props.product.type}`,
+ message: `To protect your costs and ours, we've automatically applied a $${currentPlan?.initial_billing_limit} billing limit for ${props.product.name}.`,
+ action: {
+ onClick: () => {
+ actions.setIsEditingBillingLimit(true)
+ setTimeout(() => {
+ if (props.billingLimitInputRef?.current) {
+ props.billingLimitInputRef?.current.focus()
+ props.billingLimitInputRef?.current.scrollIntoView({
+ behavior: 'smooth',
+ block: 'nearest',
+ })
+ }
+ }, 0)
+ },
+ children: 'Update billing limit',
+ },
+ })
+ }
+ }
+ },
+ })),
+ events(({ actions, values }) => ({
+ afterMount: () => {
+ actions.setScrollToProductKey(values.scrollToProductKey)
+ },
})),
])
diff --git a/frontend/src/scenes/cohorts/Cohort.tsx b/frontend/src/scenes/cohorts/Cohort.tsx
index d597f2a93964a..2286edee5e429 100644
--- a/frontend/src/scenes/cohorts/Cohort.tsx
+++ b/frontend/src/scenes/cohorts/Cohort.tsx
@@ -1,8 +1,10 @@
-import { cohortSceneLogic } from './cohortSceneLogic'
import 'antd/lib/dropdown/style/index.css'
-import { SceneExport } from 'scenes/sceneTypes'
+
import { CohortEdit } from 'scenes/cohorts/CohortEdit'
+import { SceneExport } from 'scenes/sceneTypes'
+
import { CohortLogicProps } from './cohortEditLogic'
+import { cohortSceneLogic } from './cohortSceneLogic'
export const scene: SceneExport = {
component: Cohort,
diff --git a/frontend/src/scenes/cohorts/CohortEdit.tsx b/frontend/src/scenes/cohorts/CohortEdit.tsx
index 72774547a2057..edbf5ddd46559 100644
--- a/frontend/src/scenes/cohorts/CohortEdit.tsx
+++ b/frontend/src/scenes/cohorts/CohortEdit.tsx
@@ -1,34 +1,36 @@
-import { CohortLogicProps, cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
+import { LemonDivider } from '@posthog/lemon-ui'
+import { UploadFile } from 'antd/es/upload/interface'
+import Dragger from 'antd/lib/upload/Dragger'
import { useActions, useValues } from 'kea'
-import { userLogic } from 'scenes/userLogic'
-import { PageHeader } from 'lib/components/PageHeader'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { Form } from 'kea-forms'
import { router } from 'kea-router'
-import { urls } from 'scenes/urls'
-import { Divider } from 'antd'
+import { NotFound } from 'lib/components/NotFound'
+import { PageHeader } from 'lib/components/PageHeader'
+import { CohortTypeEnum } from 'lib/constants'
import { Field } from 'lib/forms/Field'
+import { useFeatureFlag } from 'lib/hooks/useFeatureFlag'
+import { IconUploadFile } from 'lib/lemon-ui/icons'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { More } from 'lib/lemon-ui/LemonButton/More'
import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
+import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'
import { LemonSelect } from 'lib/lemon-ui/LemonSelect'
-import { COHORT_TYPE_OPTIONS } from 'scenes/cohorts/CohortFilters/constants'
-import { CohortTypeEnum } from 'lib/constants'
-import { AvailableFeature, NotebookNodeType } from '~/types'
import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea'
-import Dragger from 'antd/lib/upload/Dragger'
-import { UploadFile } from 'antd/es/upload/interface'
-import { IconUploadFile } from 'lib/lemon-ui/icons'
-import { CohortCriteriaGroups } from 'scenes/cohorts/CohortFilters/CohortCriteriaGroups'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
-import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel'
-import { Form } from 'kea-forms'
-import { NotFound } from 'lib/components/NotFound'
-import { Query } from '~/queries/Query/Query'
import { pluralize } from 'lib/utils'
-import { LemonDivider } from '@posthog/lemon-ui'
-import { AndOrFilterSelect } from '~/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect'
-import { More } from 'lib/lemon-ui/LemonButton/More'
+import { cohortEditLogic, CohortLogicProps } from 'scenes/cohorts/cohortEditLogic'
+import { CohortCriteriaGroups } from 'scenes/cohorts/CohortFilters/CohortCriteriaGroups'
+import { COHORT_TYPE_OPTIONS } from 'scenes/cohorts/CohortFilters/constants'
import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton'
+import { urls } from 'scenes/urls'
+import { userLogic } from 'scenes/userLogic'
+
+import { AndOrFilterSelect } from '~/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect'
+import { Query } from '~/queries/Query/Query'
+import { AvailableFeature, NotebookNodeType } from '~/types'
export function CohortEdit({ id }: CohortLogicProps): JSX.Element {
+ const is3000 = useFeatureFlag('POSTHOG_3000')
const logicProps = { id }
const logic = cohortEditLogic(logicProps)
const { deleteCohort, setOuterGroupsType, setQuery, duplicateCohort } = useActions(logic)
@@ -126,8 +128,8 @@ export function CohortEdit({ id }: CohortLogicProps): JSX.Element {
}
/>
-
-
+ {!is3000 &&
}
+
@@ -211,7 +213,7 @@ export function CohortEdit({ id }: CohortLogicProps): JSX.Element {
) : (
<>
-
+
Matching criteria
@@ -236,7 +238,7 @@ export function CohortEdit({ id }: CohortLogicProps): JSX.Element {
{/* The typeof here is needed to pass the cohort id to the query below. Using `isNewCohort` won't work */}
{typeof cohort.id === 'number' && (
<>
-
+
Persons in this cohort
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx
index 1535f6974e591..4a57b3dc2beed 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaGroups.tsx
@@ -1,17 +1,19 @@
import './CohortCriteriaGroups.scss'
-import { criteriaToBehavioralFilterType, isCohortCriteriaGroup } from 'scenes/cohorts/cohortUtils'
+
+import clsx from 'clsx'
+import { useActions, useValues } from 'kea'
import { Group } from 'kea-forms'
import { Field as KeaField } from 'kea-forms/lib/components'
-import clsx from 'clsx'
-import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark'
-import { alphabet } from 'lib/utils'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { IconCopy, IconDelete, IconPlusMini } from 'lib/lemon-ui/icons'
-import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
-import { useActions, useValues } from 'kea'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
+import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark'
+import { alphabet } from 'lib/utils'
+import { cohortEditLogic, CohortLogicProps } from 'scenes/cohorts/cohortEditLogic'
import { CohortCriteriaRowBuilder } from 'scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder'
-import { CohortLogicProps, cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
+import { criteriaToBehavioralFilterType, isCohortCriteriaGroup } from 'scenes/cohorts/cohortUtils'
+
import { AndOrFilterSelect } from '~/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect'
export function CohortCriteriaGroups(logicProps: CohortLogicProps): JSX.Element {
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.stories.tsx
index 684a4a78a529a..e87ba2481f9c2 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.stories.tsx
@@ -1,17 +1,18 @@
-import { useState } from 'react'
import { Meta } from '@storybook/react'
+import { useMountedLogic } from 'kea'
+import { Form } from 'kea-forms'
+import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator'
+import { useState } from 'react'
+import { cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
import {
CohortCriteriaRowBuilder,
CohortCriteriaRowBuilderProps,
} from 'scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder'
-import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator'
-import { useMountedLogic } from 'kea'
+import { BehavioralFilterType } from 'scenes/cohorts/CohortFilters/types'
+
import { actionsModel } from '~/models/actionsModel'
import { cohortsModel } from '~/models/cohortsModel'
-import { BehavioralFilterType } from 'scenes/cohorts/CohortFilters/types'
import { BehavioralEventType } from '~/types'
-import { Form } from 'kea-forms'
-import { cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
const meta: Meta = {
title: 'Filters/Cohort Filters/Row Builder',
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.tsx
index 4f7209a7d5583..9b5968364a232 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder.tsx
@@ -1,16 +1,18 @@
import './CohortCriteriaRowBuilder.scss'
-import { BehavioralFilterType, CohortFieldProps, Field, FilterType } from 'scenes/cohorts/CohortFilters/types'
-import { renderField, ROWS } from 'scenes/cohorts/CohortFilters/constants'
-import { Col, Divider } from 'antd'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { IconCopy, IconDelete } from 'lib/lemon-ui/icons'
-import { AnyCohortCriteriaType, BehavioralEventType, FilterLogicalOperator } from '~/types'
+
+import { Divider } from 'antd'
import clsx from 'clsx'
+import { useActions } from 'kea'
import { Field as KeaField } from 'kea-forms'
+import { IconCopy, IconDelete } from 'lib/lemon-ui/icons'
import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
-import { useActions } from 'kea'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { cohortEditLogic, CohortLogicProps } from 'scenes/cohorts/cohortEditLogic'
+import { renderField, ROWS } from 'scenes/cohorts/CohortFilters/constants'
+import { BehavioralFilterType, CohortFieldProps, Field, FilterType } from 'scenes/cohorts/CohortFilters/types'
import { cleanCriteria } from 'scenes/cohorts/cohortUtils'
-import { CohortLogicProps, cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
+
+import { AnyCohortCriteriaType, BehavioralEventType, FilterLogicalOperator } from '~/types'
export interface CohortCriteriaRowBuilderProps {
id: CohortLogicProps['id']
@@ -38,7 +40,7 @@ export function CohortCriteriaRowBuilder({
const renderFieldComponent = (_field: Field, i: number): JSX.Element => {
return (
-
+
{renderField[_field.type]({
fieldKey: _field.fieldKey,
criteria,
@@ -46,7 +48,7 @@ export function CohortCriteriaRowBuilder({
...(_field.groupTypeFieldKey ? { groupTypeFieldKey: _field.groupTypeFieldKey } : {}),
onChange: (newCriteria) => setCriteria(newCriteria, groupIndex, index),
} as CohortFieldProps)}
-
+
)
}
@@ -95,7 +97,7 @@ export function CohortCriteriaRowBuilder({
}}
>
<>
-
+
{renderField[FilterType.Behavioral]({
fieldKey: 'value',
criteria,
@@ -104,7 +106,7 @@ export function CohortCriteriaRowBuilder({
onChangeType?.(newCriteria['value'] ?? BehavioralEventType.PerformEvent)
},
})}
-
+
>
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortField.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortField.tsx
index 31da065eb5cde..ed2a8474d5a9b 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortField.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortField.tsx
@@ -1,11 +1,15 @@
import './CohortField.scss'
-import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton'
-import { useMemo } from 'react'
-import { cohortFieldLogic } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
+
+import clsx from 'clsx'
import { useActions, useValues } from 'kea'
-import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
+import { PropertyValue } from 'lib/components/PropertyFilters/components/PropertyValue'
import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types'
import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover'
+import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton'
+import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
+import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput'
+import { useMemo } from 'react'
+import { cohortFieldLogic } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
import {
CohortFieldBaseProps,
CohortNumberFieldProps,
@@ -14,9 +18,7 @@ import {
CohortTaxonomicFieldProps,
CohortTextFieldProps,
} from 'scenes/cohorts/CohortFilters/types'
-import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
-import clsx from 'clsx'
-import { PropertyValue } from 'lib/components/PropertyFilters/components/PropertyValue'
+
import { PropertyFilterType, PropertyFilterValue, PropertyOperator } from '~/types'
let uniqueMemoizedIndex = 0
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortNumberField.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortNumberField.stories.tsx
index 3a671f06c009c..3b61333f8914a 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortNumberField.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortNumberField.stories.tsx
@@ -1,10 +1,11 @@
-import { useState } from 'react'
import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { CohortNumberField } from './CohortField'
-import { renderField } from 'scenes/cohorts/CohortFilters/constants'
-import { CohortNumberFieldProps, FilterType } from 'scenes/cohorts/CohortFilters/types'
import { useMountedLogic } from 'kea'
+import { useState } from 'react'
import { cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
+import { renderField } from 'scenes/cohorts/CohortFilters/constants'
+import { CohortNumberFieldProps, FilterType } from 'scenes/cohorts/CohortFilters/types'
+
+import { CohortNumberField } from './CohortField'
type Story = StoryObj
const meta: Meta = {
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortPersonPropertiesValuesField.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortPersonPropertiesValuesField.stories.tsx
index e76421655908c..7ff9795e439f4 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortPersonPropertiesValuesField.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortPersonPropertiesValuesField.stories.tsx
@@ -1,10 +1,12 @@
-import { useState } from 'react'
import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { CohortPersonPropertiesValuesField } from './CohortField'
+import { useState } from 'react'
import { renderField } from 'scenes/cohorts/CohortFilters/constants'
import { CohortPersonPropertiesValuesFieldProps, FilterType } from 'scenes/cohorts/CohortFilters/types'
+
import { PropertyOperator } from '~/types'
+import { CohortPersonPropertiesValuesField } from './CohortField'
+
type Story = StoryObj
const meta: Meta = {
title: 'Filters/Cohort Filters/Fields/Person Properties',
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortSelectorField.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortSelectorField.stories.tsx
index 32a18b3219beb..566d691eced0e 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortSelectorField.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortSelectorField.stories.tsx
@@ -1,8 +1,9 @@
-import { useState } from 'react'
import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { CohortSelectorField } from './CohortField'
+import { useState } from 'react'
import { CohortSelectorFieldProps, FieldOptionsType } from 'scenes/cohorts/CohortFilters/types'
+import { CohortSelectorField } from './CohortField'
+
type Story = StoryObj
const meta: Meta = {
title: 'Filters/Cohort Filters/Fields/Select',
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortTaxonomicField.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortTaxonomicField.stories.tsx
index e80b150292d33..69e9421c43aa1 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortTaxonomicField.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortTaxonomicField.stories.tsx
@@ -1,13 +1,15 @@
-import { useState } from 'react'
import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { CohortTaxonomicField } from './CohortField'
-import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
-import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator'
import { useMountedLogic } from 'kea'
-import { actionsModel } from '~/models/actionsModel'
+import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator'
+import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { useState } from 'react'
import { renderField } from 'scenes/cohorts/CohortFilters/constants'
import { CohortTaxonomicFieldProps, FilterType } from 'scenes/cohorts/CohortFilters/types'
+import { actionsModel } from '~/models/actionsModel'
+
+import { CohortTaxonomicField } from './CohortField'
+
type Story = StoryObj
const meta: Meta = {
title: 'Filters/Cohort Filters/Fields/Taxonomic',
diff --git a/frontend/src/scenes/cohorts/CohortFilters/CohortTextField.stories.tsx b/frontend/src/scenes/cohorts/CohortFilters/CohortTextField.stories.tsx
index e119a31370cf5..95044ccf51cad 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/CohortTextField.stories.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/CohortTextField.stories.tsx
@@ -1,8 +1,9 @@
import { Meta, StoryFn, StoryObj } from '@storybook/react'
-import { CohortTextField } from './CohortField'
import { renderField } from 'scenes/cohorts/CohortFilters/constants'
import { CohortTextFieldProps, FilterType } from 'scenes/cohorts/CohortFilters/types'
+import { CohortTextField } from './CohortField'
+
type Story = StoryObj
const meta: Meta = {
title: 'Filters/Cohort Filters/Fields/Text',
diff --git a/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.test.ts b/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.test.ts
index c6647cb5a6b7a..f5d9933748cd2 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.test.ts
+++ b/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.test.ts
@@ -1,11 +1,12 @@
-import { cohortFieldLogic, CohortFieldLogicProps } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
-import { useMocks } from '~/mocks/jest'
-import { initKeaTests } from '~/test/init'
import { expectLogic } from 'kea-test-utils'
-import { groupsModel } from '~/models/groupsModel'
import { MOCK_GROUP_TYPES } from 'lib/api.mock'
-import { FieldOptionsType } from 'scenes/cohorts/CohortFilters/types'
+import { cohortFieldLogic, CohortFieldLogicProps } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
import { FIELD_VALUES } from 'scenes/cohorts/CohortFilters/constants'
+import { FieldOptionsType } from 'scenes/cohorts/CohortFilters/types'
+
+import { useMocks } from '~/mocks/jest'
+import { groupsModel } from '~/models/groupsModel'
+import { initKeaTests } from '~/test/init'
describe('cohortFieldLogic', () => {
let logic: ReturnType
diff --git a/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.ts b/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.ts
index 7ab38207877e3..fd304edb571f8 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.ts
+++ b/frontend/src/scenes/cohorts/CohortFilters/cohortFieldLogic.ts
@@ -1,15 +1,17 @@
-import { actions, kea, key, connect, propsChanged, listeners, path, props, reducers, selectors } from 'kea'
-import { BehavioralFilterKey, FieldOptionsType, FieldValues } from 'scenes/cohorts/CohortFilters/types'
+import { actions, connect, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea'
+import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { objectsEqual } from 'lib/utils'
import { FIELD_VALUES, SCALE_FIELD_VALUES } from 'scenes/cohorts/CohortFilters/constants'
+import { BehavioralFilterKey, FieldOptionsType, FieldValues } from 'scenes/cohorts/CohortFilters/types'
+import { cleanBehavioralTypeCriteria, resolveCohortFieldValue } from 'scenes/cohorts/cohortUtils'
+import { userLogic } from 'scenes/userLogic'
+
+import { actionsModel } from '~/models/actionsModel'
+import { cohortsModel } from '~/models/cohortsModel'
import { groupsModel } from '~/models/groupsModel'
import { ActorGroupType, AnyCohortCriteriaType, AvailableFeature } from '~/types'
+
import type { cohortFieldLogicType } from './cohortFieldLogicType'
-import { cleanBehavioralTypeCriteria, resolveCohortFieldValue } from 'scenes/cohorts/cohortUtils'
-import { cohortsModel } from '~/models/cohortsModel'
-import { actionsModel } from '~/models/actionsModel'
-import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
-import { objectsEqual } from 'lib/utils'
-import { userLogic } from 'scenes/userLogic'
export interface CohortFieldLogicProps {
cohortFilterLogicKey: string
diff --git a/frontend/src/scenes/cohorts/CohortFilters/constants.tsx b/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
index d0ed52f9f26f7..dc17d1b7883ea 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
+++ b/frontend/src/scenes/cohorts/CohortFilters/constants.tsx
@@ -1,3 +1,13 @@
+import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { CohortTypeEnum, PROPERTY_MATCH_TYPE } from 'lib/constants'
+import { LemonSelectOptions } from 'lib/lemon-ui/LemonSelect'
+import {
+ CohortNumberField,
+ CohortPersonPropertiesValuesField,
+ CohortSelectorField,
+ CohortTaxonomicField,
+ CohortTextField,
+} from 'scenes/cohorts/CohortFilters/CohortField'
import {
BehavioralFilterKey,
BehavioralFilterType,
@@ -12,6 +22,7 @@ import {
FilterType,
Row,
} from 'scenes/cohorts/CohortFilters/types'
+
import {
ActorGroupType,
BaseMathType,
@@ -27,16 +38,6 @@ import {
TimeUnitType,
ValueOptionType,
} from '~/types'
-import {
- CohortNumberField,
- CohortPersonPropertiesValuesField,
- CohortSelectorField,
- CohortTaxonomicField,
- CohortTextField,
-} from 'scenes/cohorts/CohortFilters/CohortField'
-import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
-import { LemonSelectOptions } from 'lib/lemon-ui/LemonSelect'
-import { CohortTypeEnum, PROPERTY_MATCH_TYPE } from 'lib/constants'
/*
* Cohort filters are broken down into 3 layers of components.
diff --git a/frontend/src/scenes/cohorts/CohortFilters/types.ts b/frontend/src/scenes/cohorts/CohortFilters/types.ts
index feb320cd340ce..f66717385bd6b 100644
--- a/frontend/src/scenes/cohorts/CohortFilters/types.ts
+++ b/frontend/src/scenes/cohorts/CohortFilters/types.ts
@@ -1,3 +1,6 @@
+import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { CohortFieldLogicProps } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
+
import {
AnyCohortCriteriaType,
BehavioralCohortType,
@@ -6,8 +9,6 @@ import {
PropertyFilterValue,
PropertyOperator,
} from '~/types'
-import { CohortFieldLogicProps } from 'scenes/cohorts/CohortFilters/cohortFieldLogic'
-import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
export enum FilterType {
Behavioral = 'behavioral',
diff --git a/frontend/src/scenes/cohorts/Cohorts.tsx b/frontend/src/scenes/cohorts/Cohorts.tsx
index 5966ef782dfcd..0ed8f9efe59d0 100644
--- a/frontend/src/scenes/cohorts/Cohorts.tsx
+++ b/frontend/src/scenes/cohorts/Cohorts.tsx
@@ -1,24 +1,27 @@
-import { useState } from 'react'
-import { cohortsModel } from '../../models/cohortsModel'
-import { useValues, useActions } from 'kea'
-import { AvailableFeature, CohortType, ProductKey } from '~/types'
import './Cohorts.scss'
+
+import { LemonInput } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { combineUrl, router } from 'kea-router'
+import { ListHog } from 'lib/components/hedgehogs'
+import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
+import { FEATURE_FLAGS } from 'lib/constants'
+import { dayjs } from 'lib/dayjs'
+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 { Link } from 'lib/lemon-ui/Link'
-import { dayjs } from 'lib/dayjs'
import { Spinner } from 'lib/lemon-ui/Spinner/Spinner'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { useState } from 'react'
import { urls } from 'scenes/urls'
-import { LemonTable, LemonTableColumns, LemonTableColumn } from 'lib/lemon-ui/LemonTable'
import { userLogic } from 'scenes/userLogic'
-import { More } from 'lib/lemon-ui/LemonButton/More'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
-import { combineUrl, router } from 'kea-router'
-import { LemonInput } from '@posthog/lemon-ui'
-import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
-import { FEATURE_FLAGS } from 'lib/constants'
-import { ListHog } from 'lib/components/hedgehogs'
+
+import { AvailableFeature, CohortType, ProductKey } from '~/types'
+
+import { cohortsModel } from '../../models/cohortsModel'
export function Cohorts(): JSX.Element {
const { cohorts, cohortsSearch, cohortsLoading } = useValues(cohortsModel)
diff --git a/frontend/src/scenes/cohorts/cohortEditLogic.test.ts b/frontend/src/scenes/cohorts/cohortEditLogic.test.ts
index abab2aacfdae5..94f6910e1b99b 100644
--- a/frontend/src/scenes/cohorts/cohortEditLogic.test.ts
+++ b/frontend/src/scenes/cohorts/cohortEditLogic.test.ts
@@ -1,12 +1,17 @@
-import { initKeaTests } from '~/test/init'
+import { router } from 'kea-router'
import { expectLogic, partial } from 'kea-test-utils'
-import { useMocks } from '~/mocks/jest'
-import { mockCohort } from '~/test/mocks'
-import { teamLogic } from 'scenes/teamLogic'
import { api } from 'lib/api.mock'
-import { cohortsModel } from '~/models/cohortsModel'
-import { router } from 'kea-router'
+import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
+import { cohortEditLogic, CohortLogicProps } from 'scenes/cohorts/cohortEditLogic'
+import { CRITERIA_VALIDATIONS, NEW_CRITERIA, ROWS } from 'scenes/cohorts/CohortFilters/constants'
+import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types'
+import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'
+
+import { useMocks } from '~/mocks/jest'
+import { cohortsModel } from '~/models/cohortsModel'
+import { initKeaTests } from '~/test/init'
+import { mockCohort } from '~/test/mocks'
import {
BehavioralEventType,
BehavioralLifecycleType,
@@ -15,10 +20,6 @@ import {
PropertyOperator,
TimeUnitType,
} from '~/types'
-import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types'
-import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
-import { CRITERIA_VALIDATIONS, NEW_CRITERIA, ROWS } from 'scenes/cohorts/CohortFilters/constants'
-import { CohortLogicProps, cohortEditLogic } from 'scenes/cohorts/cohortEditLogic'
describe('cohortEditLogic', () => {
let logic: ReturnType
diff --git a/frontend/src/scenes/cohorts/cohortEditLogic.ts b/frontend/src/scenes/cohorts/cohortEditLogic.ts
index 58b7d6af4c111..15794de4b6d24 100644
--- a/frontend/src/scenes/cohorts/cohortEditLogic.ts
+++ b/frontend/src/scenes/cohorts/cohortEditLogic.ts
@@ -1,22 +1,12 @@
import { actions, afterMount, beforeUnmount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea'
+import { forms } from 'kea-forms'
+import { loaders } from 'kea-loaders'
+import { actionToUrl, router } from 'kea-router'
import api from 'lib/api'
-import { cohortsModel, processCohort } from '~/models/cohortsModel'
import { ENTITY_MATCH_TYPE, FEATURE_FLAGS } from 'lib/constants'
-import {
- AnyCohortCriteriaType,
- AnyCohortGroupType,
- CohortCriteriaGroupFilter,
- CohortGroupType,
- CohortType,
- FilterLogicalOperator,
- PropertyFilterType,
-} from '~/types'
-import { personsLogic } from 'scenes/persons/personsLogic'
import { lemonToast } from 'lib/lemon-ui/lemonToast'
-import { urls } from 'scenes/urls'
-import { actionToUrl, router } from 'kea-router'
-import { loaders } from 'kea-loaders'
-import { forms } from 'kea-forms'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { NEW_COHORT, NEW_CRITERIA, NEW_CRITERIA_GROUP } from 'scenes/cohorts/CohortFilters/constants'
import {
applyAllCriteriaGroup,
applyAllNestedCriteria,
@@ -25,11 +15,23 @@ import {
isCohortCriteriaGroup,
validateGroup,
} from 'scenes/cohorts/cohortUtils'
-import { NEW_COHORT, NEW_CRITERIA, NEW_CRITERIA_GROUP } from 'scenes/cohorts/CohortFilters/constants'
-import type { cohortEditLogicType } from './cohortEditLogicType'
+import { personsLogic } from 'scenes/persons/personsLogic'
+import { urls } from 'scenes/urls'
+
+import { cohortsModel, processCohort } from '~/models/cohortsModel'
import { DataTableNode, Node, NodeKind } from '~/queries/schema'
import { isDataTableNode } from '~/queries/utils'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import {
+ AnyCohortCriteriaType,
+ AnyCohortGroupType,
+ CohortCriteriaGroupFilter,
+ CohortGroupType,
+ CohortType,
+ FilterLogicalOperator,
+ PropertyFilterType,
+} from '~/types'
+
+import type { cohortEditLogicType } from './cohortEditLogicType'
export type CohortLogicProps = {
id?: CohortType['id']
diff --git a/frontend/src/scenes/cohorts/cohortSceneLogic.ts b/frontend/src/scenes/cohorts/cohortSceneLogic.ts
index 1af9d5cf7adfc..a1b604ffb484e 100644
--- a/frontend/src/scenes/cohorts/cohortSceneLogic.ts
+++ b/frontend/src/scenes/cohorts/cohortSceneLogic.ts
@@ -1,11 +1,12 @@
import { kea, key, path, props, selectors } from 'kea'
-import { Breadcrumb } from '~/types'
+import { Scene } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'
+
import { cohortsModel } from '~/models/cohortsModel'
-import { CohortLogicProps } from './cohortEditLogic'
+import { Breadcrumb } from '~/types'
+import { CohortLogicProps } from './cohortEditLogic'
import type { cohortSceneLogicType } from './cohortSceneLogicType'
-import { Scene } from 'scenes/sceneTypes'
export const cohortSceneLogic = kea([
props({} as CohortLogicProps),
diff --git a/frontend/src/scenes/cohorts/cohortUtils.tsx b/frontend/src/scenes/cohorts/cohortUtils.tsx
index 6c13ba3e5adc6..d8d701daeb13a 100644
--- a/frontend/src/scenes/cohorts/cohortUtils.tsx
+++ b/frontend/src/scenes/cohorts/cohortUtils.tsx
@@ -1,3 +1,16 @@
+import equal from 'fast-deep-equal'
+import { DeepPartialMap, ValidationErrorType } from 'kea-forms'
+import { ENTITY_MATCH_TYPE, PROPERTY_MATCH_TYPE } from 'lib/constants'
+import { areObjectValuesEmpty, calculateDays, isNumeric } from 'lib/utils'
+import { BEHAVIORAL_TYPE_TO_LABEL, CRITERIA_VALIDATIONS, ROWS } from 'scenes/cohorts/CohortFilters/constants'
+import {
+ BehavioralFilterKey,
+ BehavioralFilterType,
+ CohortClientErrors,
+ FieldWithFieldKey,
+ FilterType,
+} from 'scenes/cohorts/CohortFilters/types'
+
import {
ActionType,
AnyCohortCriteriaType,
@@ -12,18 +25,6 @@ import {
PropertyOperator,
TimeUnitType,
} from '~/types'
-import { ENTITY_MATCH_TYPE, PROPERTY_MATCH_TYPE } from 'lib/constants'
-import {
- BehavioralFilterKey,
- BehavioralFilterType,
- CohortClientErrors,
- FieldWithFieldKey,
- FilterType,
-} from 'scenes/cohorts/CohortFilters/types'
-import { areObjectValuesEmpty, calculateDays, isNumeric } from 'lib/utils'
-import { DeepPartialMap, ValidationErrorType } from 'kea-forms'
-import equal from 'fast-deep-equal'
-import { BEHAVIORAL_TYPE_TO_LABEL, CRITERIA_VALIDATIONS, ROWS } from 'scenes/cohorts/CohortFilters/constants'
export function cleanBehavioralTypeCriteria(criteria: AnyCohortCriteriaType): AnyCohortCriteriaType {
let type = undefined
@@ -89,7 +90,7 @@ export function isValidCohortGroup(criteria: AnyCohortGroupType): boolean {
export function createCohortFormData(cohort: CohortType): FormData {
const rawCohort = {
...(cohort.name ? { name: cohort.name } : {}),
- ...(cohort.description ? { description: cohort.description } : {}),
+ ...{ description: cohort.description ?? '' },
...(cohort.csv ? { csv: cohort.csv } : {}),
...(cohort.is_static ? { is_static: cohort.is_static } : {}),
filters: JSON.stringify(
diff --git a/frontend/src/scenes/dashboard/Dashboard.scss b/frontend/src/scenes/dashboard/Dashboard.scss
index f35d1decb41af..fe4c80e270a4a 100644
--- a/frontend/src/scenes/dashboard/Dashboard.scss
+++ b/frontend/src/scenes/dashboard/Dashboard.scss
@@ -37,3 +37,11 @@
}
}
}
+
+.DashboardTemplates__option {
+ border: 1px solid var(--border);
+
+ &:hover {
+ border-color: var(--primary-3000-hover);
+ }
+}
diff --git a/frontend/src/scenes/dashboard/Dashboard.tsx b/frontend/src/scenes/dashboard/Dashboard.tsx
index 03418ed2e140c..cad193b0c0866 100644
--- a/frontend/src/scenes/dashboard/Dashboard.tsx
+++ b/frontend/src/scenes/dashboard/Dashboard.tsx
@@ -1,26 +1,29 @@
-import { useEffect } from 'react'
+import './Dashboard.scss'
+
+import { IconCalendar } from '@posthog/icons'
+import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
import { BindLogic, useActions, useValues } from 'kea'
-import { dashboardLogic, DashboardLogicProps } from 'scenes/dashboard/dashboardLogic'
-import { DashboardItems } from 'scenes/dashboard/DashboardItems'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
-import './Dashboard.scss'
-import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys'
-import { DashboardMode, DashboardPlacement, DashboardType } from '~/types'
-import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
-import { EmptyDashboardComponent } from './EmptyDashboardComponent'
import { NotFound } from 'lib/components/NotFound'
-import { DashboardReloadAction, LastRefreshText } from 'scenes/dashboard/DashboardReloadAction'
-import { SceneExport } from 'scenes/sceneTypes'
-import { InsightErrorState } from 'scenes/insights/EmptyStates'
-import { DashboardHeader } from './DashboardHeader'
import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters'
-import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
-import { groupsModel } from '../../models/groupsModel'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { FEATURE_FLAGS } from 'lib/constants'
+import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+import { useEffect } from 'react'
+import { DashboardItems } from 'scenes/dashboard/DashboardItems'
+import { dashboardLogic, DashboardLogicProps } from 'scenes/dashboard/dashboardLogic'
+import { DashboardReloadAction, LastRefreshText } from 'scenes/dashboard/DashboardReloadAction'
+import { InsightErrorState } from 'scenes/insights/EmptyStates'
+import { SceneExport } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'
-import { IconCalendar } from '@posthog/icons'
+
+import { DashboardMode, DashboardPlacement, DashboardType } from '~/types'
+
+import { groupsModel } from '../../models/groupsModel'
+import { DashboardHeader } from './DashboardHeader'
+import { EmptyDashboardComponent } from './EmptyDashboardComponent'
interface DashboardProps {
id?: string
diff --git a/frontend/src/scenes/dashboard/DashboardCollaborators.tsx b/frontend/src/scenes/dashboard/DashboardCollaborators.tsx
index 4f13b597f8fee..63e7c0547f504 100644
--- a/frontend/src/scenes/dashboard/DashboardCollaborators.tsx
+++ b/frontend/src/scenes/dashboard/DashboardCollaborators.tsx
@@ -1,17 +1,19 @@
import { useActions, useValues } from 'kea'
-import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini'
+import { usersLemonSelectOptions } from 'lib/components/UserSelectItem'
+import { DashboardPrivilegeLevel, DashboardRestrictionLevel, privilegeLevelToName } from 'lib/constants'
import { IconDelete, IconLock, IconLockOpen } from 'lib/lemon-ui/icons'
-import { AvailableFeature, DashboardType, FusedDashboardCollaboratorType, UserType } from '~/types'
-import { DashboardRestrictionLevel, privilegeLevelToName, DashboardPrivilegeLevel } from 'lib/constants'
+import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonSelect, LemonSelectOptions } from 'lib/lemon-ui/LemonSelect'
-import { dashboardCollaboratorsLogic } from './dashboardCollaboratorsLogic'
+import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple'
import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture'
import { Tooltip } from 'lib/lemon-ui/Tooltip'
-import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini'
-import { LemonBanner } from 'lib/lemon-ui/LemonBanner'
-import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple'
-import { usersLemonSelectOptions } from 'lib/components/UserSelectItem'
+import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
+
+import { AvailableFeature, DashboardType, FusedDashboardCollaboratorType, UserType } from '~/types'
+
+import { dashboardCollaboratorsLogic } from './dashboardCollaboratorsLogic'
export const DASHBOARD_RESTRICTION_OPTIONS: LemonSelectOptions = [
{
diff --git a/frontend/src/scenes/dashboard/DashboardHeader.tsx b/frontend/src/scenes/dashboard/DashboardHeader.tsx
index 113479b97d1ee..6d94e94830fe6 100644
--- a/frontend/src/scenes/dashboard/DashboardHeader.tsx
+++ b/frontend/src/scenes/dashboard/DashboardHeader.tsx
@@ -1,38 +1,40 @@
import { useActions, useValues } from 'kea'
+import { router } from 'kea-router'
+import { TextCardModal } from 'lib/components/Cards/TextCard/TextCardModal'
import { EditableField } from 'lib/components/EditableField/EditableField'
+import { ExportButton, ExportButtonItem } from 'lib/components/ExportButton/ExportButton'
+import { FlaggedFeature } from 'lib/components/FlaggedFeature'
import { FullScreen } from 'lib/components/FullScreen'
+import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
+import { PageHeader } from 'lib/components/PageHeader'
+import { SharingModal } from 'lib/components/Sharing/SharingModal'
+import { SubscribeButton, SubscriptionsModal } from 'lib/components/Subscriptions/SubscriptionsModal'
+import { privilegeLevelToName } from 'lib/constants'
+import { IconLock } from 'lib/lemon-ui/icons'
import { LemonButton, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton'
import { More } from 'lib/lemon-ui/LemonButton/More'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
-import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
-import { PageHeader } from 'lib/components/PageHeader'
+import { isLemonSelectSection } from 'lib/lemon-ui/LemonSelect'
+import { ProfileBubbles } from 'lib/lemon-ui/ProfilePicture/ProfileBubbles'
import { humanFriendlyDetailedTime, slugify } from 'lib/utils'
import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
+import { DeleteDashboardModal } from 'scenes/dashboard/DeleteDashboardModal'
+import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic'
+import { DuplicateDashboardModal } from 'scenes/dashboard/DuplicateDashboardModal'
+import { urls } from 'scenes/urls'
+import { userLogic } from 'scenes/userLogic'
+
import { dashboardsModel } from '~/models/dashboardsModel'
+import { notebooksModel } from '~/models/notebooksModel'
+import { tagsModel } from '~/models/tagsModel'
import { AvailableFeature, DashboardMode, DashboardType, ExporterFormat } from '~/types'
-import { dashboardLogic } from './dashboardLogic'
+
import { DASHBOARD_RESTRICTION_OPTIONS } from './DashboardCollaborators'
-import { userLogic } from 'scenes/userLogic'
-import { privilegeLevelToName } from 'lib/constants'
-import { ProfileBubbles } from 'lib/lemon-ui/ProfilePicture/ProfileBubbles'
import { dashboardCollaboratorsLogic } from './dashboardCollaboratorsLogic'
-import { IconLock } from 'lib/lemon-ui/icons'
-import { urls } from 'scenes/urls'
-import { ExportButton, ExportButtonItem } from 'lib/components/ExportButton/ExportButton'
-import { SubscribeButton, SubscriptionsModal } from 'lib/components/Subscriptions/SubscriptionsModal'
-import { router } from 'kea-router'
-import { SharingModal } from 'lib/components/Sharing/SharingModal'
-import { isLemonSelectSection } from 'lib/lemon-ui/LemonSelect'
-import { TextCardModal } from 'lib/components/Cards/TextCard/TextCardModal'
-import { DeleteDashboardModal } from 'scenes/dashboard/DeleteDashboardModal'
-import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
-import { DuplicateDashboardModal } from 'scenes/dashboard/DuplicateDashboardModal'
-import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic'
-import { tagsModel } from '~/models/tagsModel'
+import { dashboardLogic } from './dashboardLogic'
import { DashboardTemplateEditor } from './DashboardTemplateEditor'
import { dashboardTemplateEditorLogic } from './dashboardTemplateEditorLogic'
-import { notebooksModel } from '~/models/notebooksModel'
-import { FlaggedFeature } from 'lib/components/FlaggedFeature'
export const DASHBOARD_CANNOT_EDIT_MESSAGE =
"You don't have edit permissions for this dashboard. Ask a dashboard collaborator with edit access to add you."
diff --git a/frontend/src/scenes/dashboard/DashboardItems.tsx b/frontend/src/scenes/dashboard/DashboardItems.tsx
index 546b142adf097..797f9db8dfc72 100644
--- a/frontend/src/scenes/dashboard/DashboardItems.tsx
+++ b/frontend/src/scenes/dashboard/DashboardItems.tsx
@@ -1,18 +1,18 @@
import './DashboardItems.scss'
-import { useRef, useState } from 'react'
-import { useActions, useValues } from 'kea'
-import { Responsive as ReactGridLayout } from 'react-grid-layout'
-
-import { DashboardMode, DashboardType, DashboardPlacement, DashboardTile } from '~/types'
-import { insightsModel } from '~/models/insightsModel'
-import { dashboardLogic, BREAKPOINT_COLUMN_COUNTS, BREAKPOINTS } from 'scenes/dashboard/dashboardLogic'
import clsx from 'clsx'
+import { useActions, useValues } from 'kea'
import { InsightCard } from 'lib/components/Cards/InsightCard'
+import { TextCard } from 'lib/components/Cards/TextCard/TextCard'
import { useResizeObserver } from 'lib/hooks/useResizeObserver'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
-import { TextCard } from 'lib/components/Cards/TextCard/TextCard'
+import { useRef, useState } from 'react'
+import { Responsive as ReactGridLayout } from 'react-grid-layout'
+import { BREAKPOINT_COLUMN_COUNTS, BREAKPOINTS, dashboardLogic } from 'scenes/dashboard/dashboardLogic'
+
+import { insightsModel } from '~/models/insightsModel'
+import { DashboardMode, DashboardPlacement, DashboardTile, DashboardType } from '~/types'
export function DashboardItems(): JSX.Element {
const {
diff --git a/frontend/src/scenes/dashboard/DashboardReloadAction.tsx b/frontend/src/scenes/dashboard/DashboardReloadAction.tsx
index 72c9d2bedebe8..0c66d8e71df4e 100644
--- a/frontend/src/scenes/dashboard/DashboardReloadAction.tsx
+++ b/frontend/src/scenes/dashboard/DashboardReloadAction.tsx
@@ -1,13 +1,13 @@
+import { LemonButtonWithSideAction, LemonDivider, LemonSwitch } from '@posthog/lemon-ui'
import { Radio, RadioChangeEvent } from 'antd'
-import { dashboardLogic, DASHBOARD_MIN_REFRESH_INTERVAL_MINUTES } from 'scenes/dashboard/dashboardLogic'
-import { useActions, useValues } from 'kea'
-import { humanFriendlyDuration } from 'lib/utils'
import clsx from 'clsx'
-import { Tooltip } from 'lib/lemon-ui/Tooltip'
+import { useActions, useValues } from 'kea'
import { dayjs } from 'lib/dayjs'
-import { LemonButtonWithSideAction, LemonDivider, LemonSwitch } from '@posthog/lemon-ui'
import { IconRefresh } from 'lib/lemon-ui/icons'
import { Spinner } from 'lib/lemon-ui/Spinner'
+import { Tooltip } from 'lib/lemon-ui/Tooltip'
+import { humanFriendlyDuration } from 'lib/utils'
+import { DASHBOARD_MIN_REFRESH_INTERVAL_MINUTES, dashboardLogic } from 'scenes/dashboard/dashboardLogic'
export const LastRefreshText = (): JSX.Element => {
const { lastRefreshed } = useValues(dashboardLogic)
diff --git a/frontend/src/scenes/dashboard/DashboardTemplateEditor.stories.tsx b/frontend/src/scenes/dashboard/DashboardTemplateEditor.stories.tsx
index c7c13ec44bc8b..58611ed1d6b76 100644
--- a/frontend/src/scenes/dashboard/DashboardTemplateEditor.stories.tsx
+++ b/frontend/src/scenes/dashboard/DashboardTemplateEditor.stories.tsx
@@ -1,4 +1,5 @@
import { Meta } from '@storybook/react'
+
import { DashboardTemplateEditor } from './DashboardTemplateEditor'
import { dashboardTemplateEditorLogic } from './dashboardTemplateEditorLogic'
diff --git a/frontend/src/scenes/dashboard/DashboardTemplateEditor.tsx b/frontend/src/scenes/dashboard/DashboardTemplateEditor.tsx
index d145d26cb396a..bd7dff72006ec 100644
--- a/frontend/src/scenes/dashboard/DashboardTemplateEditor.tsx
+++ b/frontend/src/scenes/dashboard/DashboardTemplateEditor.tsx
@@ -1,9 +1,10 @@
-import { LemonButton, LemonModal } from '@posthog/lemon-ui'
import { useMonaco } from '@monaco-editor/react'
-import { useEffect } from 'react'
+import { LemonButton, LemonModal } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
-import { dashboardTemplateEditorLogic } from './dashboardTemplateEditorLogic'
import { CodeEditor } from 'lib/components/CodeEditors'
+import { useEffect } from 'react'
+
+import { dashboardTemplateEditorLogic } from './dashboardTemplateEditorLogic'
export function DashboardTemplateEditor({ inline = false }: { inline?: boolean }): JSX.Element {
const monaco = useMonaco()
diff --git a/frontend/src/scenes/dashboard/DashboardTemplateVariables.tsx b/frontend/src/scenes/dashboard/DashboardTemplateVariables.tsx
index 13440778b192a..a2fe844aab338 100644
--- a/frontend/src/scenes/dashboard/DashboardTemplateVariables.tsx
+++ b/frontend/src/scenes/dashboard/DashboardTemplateVariables.tsx
@@ -1,7 +1,9 @@
import { LemonLabel } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter'
+
import { FilterType, InsightType } from '~/types'
+
import { dashboardTemplateVariablesLogic } from './dashboardTemplateVariablesLogic'
import { newDashboardLogic } from './newDashboardLogic'
diff --git a/frontend/src/scenes/dashboard/Dashboards.stories.tsx b/frontend/src/scenes/dashboard/Dashboards.stories.tsx
index 1794572670043..93286b38d2bb7 100644
--- a/frontend/src/scenes/dashboard/Dashboards.stories.tsx
+++ b/frontend/src/scenes/dashboard/Dashboards.stories.tsx
@@ -1,14 +1,16 @@
-import { useEffect } from 'react'
import { Meta } from '@storybook/react'
-import { mswDecorator } from '~/mocks/browser'
-import { App } from 'scenes/App'
import { router } from 'kea-router'
-import { urls } from 'scenes/urls'
+import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+import { useEffect } from 'react'
+import { App } from 'scenes/App'
+import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
+import { urls } from 'scenes/urls'
+
+import { mswDecorator } from '~/mocks/browser'
import { useAvailableFeatures } from '~/mocks/features'
import { DashboardMode } from '~/types'
-import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
-import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+
import { dashboardTemplatesLogic } from './dashboards/templates/dashboardTemplatesLogic'
const meta: Meta = {
diff --git a/frontend/src/scenes/dashboard/DeleteDashboardModal.tsx b/frontend/src/scenes/dashboard/DeleteDashboardModal.tsx
index 0ef1911307f96..035ef4e0e94ab 100644
--- a/frontend/src/scenes/dashboard/DeleteDashboardModal.tsx
+++ b/frontend/src/scenes/dashboard/DeleteDashboardModal.tsx
@@ -1,12 +1,10 @@
import { useActions, useValues } from 'kea'
-
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-
-import { LemonModal } from 'lib/lemon-ui/LemonModal'
import { Form } from 'kea-forms'
-import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
import { Field } from 'lib/forms/Field'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox'
+import { LemonModal } from 'lib/lemon-ui/LemonModal'
+import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
export function DeleteDashboardModal(): JSX.Element {
const { hideDeleteDashboardModal } = useActions(deleteDashboardLogic)
diff --git a/frontend/src/scenes/dashboard/DuplicateDashboardModal.tsx b/frontend/src/scenes/dashboard/DuplicateDashboardModal.tsx
index 61306cfc59705..46beee57edbee 100644
--- a/frontend/src/scenes/dashboard/DuplicateDashboardModal.tsx
+++ b/frontend/src/scenes/dashboard/DuplicateDashboardModal.tsx
@@ -1,9 +1,9 @@
import { useActions, useValues } from 'kea'
+import { Form } from 'kea-forms'
import { Field } from 'lib/forms/Field'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonModal } from 'lib/lemon-ui/LemonModal'
-import { Form } from 'kea-forms'
import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox'
+import { LemonModal } from 'lib/lemon-ui/LemonModal'
import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic'
export function DuplicateDashboardModal(): JSX.Element {
diff --git a/frontend/src/scenes/dashboard/EmptyDashboardComponent.tsx b/frontend/src/scenes/dashboard/EmptyDashboardComponent.tsx
index fc290a9219b1b..f7965f7ba1519 100644
--- a/frontend/src/scenes/dashboard/EmptyDashboardComponent.tsx
+++ b/frontend/src/scenes/dashboard/EmptyDashboardComponent.tsx
@@ -1,12 +1,14 @@
-import { dashboardLogic } from './dashboardLogic'
+import './EmptyDashboardComponent.scss'
+
import { useValues } from 'kea'
-import { urls } from 'scenes/urls'
+import { IconPlus } from 'lib/lemon-ui/icons'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton'
-import { IconPlus } from 'lib/lemon-ui/icons'
-import './EmptyDashboardComponent.scss'
import React from 'react'
+import { urls } from 'scenes/urls'
+
import { DASHBOARD_CANNOT_EDIT_MESSAGE } from './DashboardHeader'
+import { dashboardLogic } from './dashboardLogic'
function SkeletonCard({ children, active }: { children: React.ReactNode; active: boolean }): JSX.Element {
return (
diff --git a/frontend/src/scenes/dashboard/NewDashboardModal.tsx b/frontend/src/scenes/dashboard/NewDashboardModal.tsx
index d9b819806bc38..82dc1d2dd8011 100644
--- a/frontend/src/scenes/dashboard/NewDashboardModal.tsx
+++ b/frontend/src/scenes/dashboard/NewDashboardModal.tsx
@@ -1,19 +1,20 @@
+import './NewDashboardModal.scss'
+
+import { LemonButton } from '@posthog/lemon-ui'
+import clsx from 'clsx'
import { useActions, useMountedLogic, useValues } from 'kea'
-import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
+import { FallbackCoverImage } from 'lib/components/FallbackCoverImage/FallbackCoverImage'
import { LemonModal } from 'lib/lemon-ui/LemonModal'
-import { dashboardTemplatesLogic } from 'scenes/dashboard/dashboards/templates/dashboardTemplatesLogic'
-import { DashboardTemplateVariables } from './DashboardTemplateVariables'
-import { LemonButton } from '@posthog/lemon-ui'
-import { dashboardTemplateVariablesLogic } from './dashboardTemplateVariablesLogic'
-import { DashboardTemplateScope, DashboardTemplateType } from '~/types'
+import { pluralize } from 'lib/utils'
+import BlankDashboardHog from 'public/blank-dashboard-hog.png'
import { useState } from 'react'
+import { dashboardTemplatesLogic } from 'scenes/dashboard/dashboards/templates/dashboardTemplatesLogic'
+import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
-import { pluralize } from 'lib/utils'
+import { DashboardTemplateScope, DashboardTemplateType } from '~/types'
-import BlankDashboardHog from 'public/blank-dashboard-hog.png'
-import './NewDashboardModal.scss'
-import { FallbackCoverImage } from 'lib/components/FallbackCoverImage/FallbackCoverImage'
-import clsx from 'clsx'
+import { DashboardTemplateVariables } from './DashboardTemplateVariables'
+import { dashboardTemplateVariablesLogic } from './dashboardTemplateVariablesLogic'
function TemplateItem({
template,
diff --git a/frontend/src/scenes/dashboard/dashboardCollaboratorsLogic.ts b/frontend/src/scenes/dashboard/dashboardCollaboratorsLogic.ts
index 1ff801b17ead3..b1ede643920a2 100644
--- a/frontend/src/scenes/dashboard/dashboardCollaboratorsLogic.ts
+++ b/frontend/src/scenes/dashboard/dashboardCollaboratorsLogic.ts
@@ -1,17 +1,19 @@
+import { actions, connect, events, kea, key, path, props, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
-import { kea, props, key, path, connect, actions, reducers, selectors, events } from 'kea'
import api from 'lib/api'
import { DashboardPrivilegeLevel, DashboardRestrictionLevel } from 'lib/constants'
+import { teamMembersLogic } from 'scenes/settings/project/teamMembersLogic'
+
import {
- DashboardType,
DashboardCollaboratorType,
- UserType,
+ DashboardType,
FusedDashboardCollaboratorType,
UserBasicType,
+ UserType,
} from '~/types'
+
import type { dashboardCollaboratorsLogicType } from './dashboardCollaboratorsLogicType'
import { dashboardLogic } from './dashboardLogic'
-import { teamMembersLogic } from 'scenes/settings/project/teamMembersLogic'
export interface DashboardCollaboratorsLogicProps {
dashboardId: DashboardType['id']
diff --git a/frontend/src/scenes/dashboard/dashboardLogic.queryCancellation.test.ts b/frontend/src/scenes/dashboard/dashboardLogic.queryCancellation.test.ts
index dce00c6637c2a..ef026526841fa 100644
--- a/frontend/src/scenes/dashboard/dashboardLogic.queryCancellation.test.ts
+++ b/frontend/src/scenes/dashboard/dashboardLogic.queryCancellation.test.ts
@@ -1,15 +1,16 @@
import { expectLogic, partial } from 'kea-test-utils'
-import { initKeaTests } from '~/test/init'
+import api from 'lib/api'
+import { MOCK_TEAM_ID } from 'lib/api.mock'
+import { now } from 'lib/dayjs'
+import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
+import { boxToString, dashboardResult, insightOnDashboard, tileFromInsight } from 'scenes/dashboard/dashboardLogic.test'
+
+import { useMocks } from '~/mocks/jest'
import { dashboardsModel } from '~/models/dashboardsModel'
import { insightsModel } from '~/models/insightsModel'
-import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
+import { initKeaTests } from '~/test/init'
import { DashboardType, InsightModel, InsightShortId } from '~/types'
-import { useMocks } from '~/mocks/jest'
-import { now } from 'lib/dayjs'
-import { MOCK_TEAM_ID } from 'lib/api.mock'
-import api from 'lib/api'
-import { boxToString, dashboardResult, insightOnDashboard, tileFromInsight } from 'scenes/dashboard/dashboardLogic.test'
const seenQueryIDs: string[] = []
diff --git a/frontend/src/scenes/dashboard/dashboardLogic.test.ts b/frontend/src/scenes/dashboard/dashboardLogic.test.ts
index af0243b011440..baf761f3e98f9 100644
--- a/frontend/src/scenes/dashboard/dashboardLogic.test.ts
+++ b/frontend/src/scenes/dashboard/dashboardLogic.test.ts
@@ -1,11 +1,18 @@
// let tiles assert an insight is present in tests i.e. `tile!.insight` when it must be present for tests to pass
import { expectLogic, truth } from 'kea-test-utils'
-import { initKeaTests } from '~/test/init'
+import api from 'lib/api'
+import { MOCK_TEAM_ID } from 'lib/api.mock'
+import { dayjs, now } from 'lib/dayjs'
+import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
-import _dashboardJson from './__mocks__/dashboard.json'
+import { teamLogic } from 'scenes/teamLogic'
+
+import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea'
+import { useMocks } from '~/mocks/jest'
import { dashboardsModel } from '~/models/dashboardsModel'
import { insightsModel } from '~/models/insightsModel'
-import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
+import { DashboardFilter } from '~/queries/schema'
+import { initKeaTests } from '~/test/init'
import {
DashboardTile,
DashboardType,
@@ -16,13 +23,8 @@ import {
TextModel,
TileLayout,
} from '~/types'
-import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea'
-import { useMocks } from '~/mocks/jest'
-import { dayjs, now } from 'lib/dayjs'
-import { teamLogic } from 'scenes/teamLogic'
-import { MOCK_TEAM_ID } from 'lib/api.mock'
-import api from 'lib/api'
-import { DashboardFilter } from '~/queries/schema'
+
+import _dashboardJson from './__mocks__/dashboard.json'
const dashboardJson = _dashboardJson as any as DashboardType
diff --git a/frontend/src/scenes/dashboard/dashboardLogic.tsx b/frontend/src/scenes/dashboard/dashboardLogic.tsx
index 0ceb6c33ee07a..a35ac8752f90e 100644
--- a/frontend/src/scenes/dashboard/dashboardLogic.tsx
+++ b/frontend/src/scenes/dashboard/dashboardLogic.tsx
@@ -12,17 +12,30 @@ import {
selectors,
sharedListeners,
} from 'kea'
-import api, { ApiMethodOptions, getJSONOrThrow } from 'lib/api'
-import { dashboardsModel } from '~/models/dashboardsModel'
+import { loaders } from 'kea-loaders'
import { router, urlToAction } from 'kea-router'
-import { clearDOMTextSelection, isUserLoggedIn, shouldCancelQuery, toParams, uuid } from 'lib/utils'
-import { insightsModel } from '~/models/insightsModel'
+import api, { ApiMethodOptions, getJSONOrThrow } from 'lib/api'
import {
AUTO_REFRESH_DASHBOARD_THRESHOLD_HOURS,
DashboardPrivilegeLevel,
OrganizationMembershipLevel,
} from 'lib/constants'
+import { Dayjs, dayjs, now } from 'lib/dayjs'
+import { captureTimeToSeeData, currentSessionId, TimeToSeeDataPayload } from 'lib/internalMetrics'
+import { lemonToast } from 'lib/lemon-ui/lemonToast'
+import { Link } from 'lib/lemon-ui/Link'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { clearDOMTextSelection, isUserLoggedIn, shouldCancelQuery, toParams, uuid } from 'lib/utils'
import { DashboardEventSource, eventUsageLogic } from 'lib/utils/eventUsageLogic'
+import { Layout, Layouts } from 'react-grid-layout'
+import { calculateLayouts } from 'scenes/dashboard/tileLayouts'
+import { insightLogic } from 'scenes/insights/insightLogic'
+import { Scene } from 'scenes/sceneTypes'
+import { urls } from 'scenes/urls'
+import { userLogic } from 'scenes/userLogic'
+
+import { dashboardsModel } from '~/models/dashboardsModel'
+import { insightsModel } from '~/models/insightsModel'
import {
AnyPropertyFilter,
Breadcrumb,
@@ -39,21 +52,10 @@ import {
TextModel,
TileLayout,
} from '~/types'
-import type { dashboardLogicType } from './dashboardLogicType'
-import { Layout, Layouts } from 'react-grid-layout'
-import { insightLogic } from 'scenes/insights/insightLogic'
-import { teamLogic } from '../teamLogic'
-import { urls } from 'scenes/urls'
-import { userLogic } from 'scenes/userLogic'
-import { dayjs, now, Dayjs } from 'lib/dayjs'
-import { lemonToast } from 'lib/lemon-ui/lemonToast'
-import { Link } from 'lib/lemon-ui/Link'
-import { captureTimeToSeeData, currentSessionId, TimeToSeeDataPayload } from 'lib/internalMetrics'
+
import { getResponseBytes, sortDates } from '../insights/utils'
-import { loaders } from 'kea-loaders'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
-import { calculateLayouts } from 'scenes/dashboard/tileLayouts'
-import { Scene } from 'scenes/sceneTypes'
+import { teamLogic } from '../teamLogic'
+import type { dashboardLogicType } from './dashboardLogicType'
export const BREAKPOINTS: Record = {
sm: 1024,
diff --git a/frontend/src/scenes/dashboard/dashboardTemplateEditorLogic.ts b/frontend/src/scenes/dashboard/dashboardTemplateEditorLogic.ts
index 70c81ec1200a9..60a039058f4dc 100644
--- a/frontend/src/scenes/dashboard/dashboardTemplateEditorLogic.ts
+++ b/frontend/src/scenes/dashboard/dashboardTemplateEditorLogic.ts
@@ -2,11 +2,12 @@ import { lemonToast } from '@posthog/lemon-ui'
import { actions, afterMount, connect, kea, listeners, path, reducers } from 'kea'
import { loaders } from 'kea-loaders'
import api from 'lib/api'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+
import { DashboardTemplateEditorType, DashboardTemplateType, MonacoMarker } from '~/types'
-import { dashboardTemplatesLogic } from './dashboards/templates/dashboardTemplatesLogic'
+import { dashboardTemplatesLogic } from './dashboards/templates/dashboardTemplatesLogic'
import type { dashboardTemplateEditorLogicType } from './dashboardTemplateEditorLogicType'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
export const dashboardTemplateEditorLogic = kea([
path(['scenes', 'dashboard', 'dashboardTemplateEditorLogic']),
diff --git a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
index 96f865e8377e8..39b45617e66ac 100644
--- a/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
+++ b/frontend/src/scenes/dashboard/dashboardTemplateVariablesLogic.ts
@@ -1,8 +1,9 @@
import { actions, kea, path, props, propsChanged, reducers } from 'kea'
+import { isEmptyObject } from 'lib/utils'
+
import { DashboardTemplateVariableType, FilterType, Optional } from '~/types'
import type { dashboardTemplateVariablesLogicType } from './dashboardTemplateVariablesLogicType'
-import { isEmptyObject } from 'lib/utils'
export interface DashboardTemplateVariablesLogicProps {
variables: DashboardTemplateVariableType[]
diff --git a/frontend/src/scenes/dashboard/dashboards/Dashboards.tsx b/frontend/src/scenes/dashboard/dashboards/Dashboards.tsx
index 6c70731560a5a..f83556b385ec2 100644
--- a/frontend/src/scenes/dashboard/dashboards/Dashboards.tsx
+++ b/frontend/src/scenes/dashboard/dashboards/Dashboards.tsx
@@ -1,18 +1,19 @@
import { useActions, useValues } from 'kea'
-import { dashboardsModel } from '~/models/dashboardsModel'
-import { dashboardsLogic, DashboardsTab } from 'scenes/dashboard/dashboards/dashboardsLogic'
-import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal'
import { PageHeader } from 'lib/components/PageHeader'
-import { SceneExport } from 'scenes/sceneTypes'
import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
+import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
import { inAppPromptLogic } from 'lib/logic/inAppPrompt/inAppPromptLogic'
-import { DeleteDashboardModal } from 'scenes/dashboard/DeleteDashboardModal'
-import { DuplicateDashboardModal } from 'scenes/dashboard/DuplicateDashboardModal'
-import { NoDashboards } from 'scenes/dashboard/dashboards/NoDashboards'
+import { dashboardsLogic, DashboardsTab } from 'scenes/dashboard/dashboards/dashboardsLogic'
import { DashboardsTableContainer } from 'scenes/dashboard/dashboards/DashboardsTable'
+import { NoDashboards } from 'scenes/dashboard/dashboards/NoDashboards'
import { DashboardTemplatesTable } from 'scenes/dashboard/dashboards/templates/DashboardTemplatesTable'
-import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs'
+import { DeleteDashboardModal } from 'scenes/dashboard/DeleteDashboardModal'
+import { DuplicateDashboardModal } from 'scenes/dashboard/DuplicateDashboardModal'
+import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
+import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal'
+import { SceneExport } from 'scenes/sceneTypes'
+
+import { dashboardsModel } from '~/models/dashboardsModel'
export const scene: SceneExport = {
component: Dashboards,
diff --git a/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx b/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx
index 44143d77c9c22..00889a9e1e5aa 100644
--- a/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx
+++ b/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx
@@ -1,30 +1,32 @@
+import { IconPin, IconPinFilled, IconShare } from '@posthog/icons'
+import { LemonInput, LemonSelect } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
-import { dashboardsModel, nameCompareFunction } from '~/models/dashboardsModel'
-import { DashboardsFilters, dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic'
-import { userLogic } from 'scenes/userLogic'
-import { teamLogic } from 'scenes/teamLogic'
-import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic'
-import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
-import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
-import { AvailableFeature, DashboardBasicType, DashboardMode, DashboardType } from '~/types'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
import { DashboardPrivilegeLevel } from 'lib/constants'
-import { Link } from 'lib/lemon-ui/Link'
-import { urls } from 'scenes/urls'
-import { Tooltip } from 'lib/lemon-ui/Tooltip'
import { IconCottage, IconLock } from 'lib/lemon-ui/icons'
-import { IconPin, IconPinFilled, IconShare } from '@posthog/icons'
-import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
-import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
import { More } from 'lib/lemon-ui/LemonButton/More'
-import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
+import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown'
import { LemonRow } from 'lib/lemon-ui/LemonRow'
-import { DASHBOARD_CANNOT_EDIT_MESSAGE } from '../DashboardHeader'
-import { LemonInput, LemonSelect } from '@posthog/lemon-ui'
+import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
+import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
+import { Link } from 'lib/lemon-ui/Link'
+import { Tooltip } from 'lib/lemon-ui/Tooltip'
+import { DashboardEventSource } from 'lib/utils/eventUsageLogic'
+import { dashboardLogic } from 'scenes/dashboard/dashboardLogic'
+import { DashboardsFilters, dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic'
+import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic'
+import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic'
import { membersLogic } from 'scenes/organization/membersLogic'
-import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown'
+import { teamLogic } from 'scenes/teamLogic'
+import { urls } from 'scenes/urls'
+import { userLogic } from 'scenes/userLogic'
+
+import { dashboardsModel, nameCompareFunction } from '~/models/dashboardsModel'
+import { AvailableFeature, DashboardBasicType, DashboardMode, DashboardType } from '~/types'
+
+import { DASHBOARD_CANNOT_EDIT_MESSAGE } from '../DashboardHeader'
export function DashboardsTableContainer(): JSX.Element {
const { dashboardsLoading } = useValues(dashboardsModel)
diff --git a/frontend/src/scenes/dashboard/dashboards/NoDashboards.tsx b/frontend/src/scenes/dashboard/dashboards/NoDashboards.tsx
index 0b3dc1735d610..f8797c2e0b6da 100644
--- a/frontend/src/scenes/dashboard/dashboards/NoDashboards.tsx
+++ b/frontend/src/scenes/dashboard/dashboards/NoDashboards.tsx
@@ -1,47 +1,51 @@
import { useActions } from 'kea'
import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic'
-import { Card } from 'antd'
-// eslint-disable-next-line no-restricted-imports
-import { AppstoreAddOutlined } from '@ant-design/icons'
export const NoDashboards = (): JSX.Element => {
- const { addDashboard } = useActions(newDashboardLogic)
-
return (
Create your first dashboard:
-
- addDashboard({
- name: 'New Dashboard',
- useTemplate: '',
- })
- }
- >
-
-
-
+
)
}
+
+const Option = ({
+ title,
+ description,
+ template,
+}: {
+ title: string
+ description: string
+ template: { name: string; template: string }
+}): JSX.Element => {
+ const { addDashboard } = useActions(newDashboardLogic)
+
+ const onClick = (): void => {
+ addDashboard({
+ name: template.name,
+ useTemplate: template.template,
+ })
+ }
+
+ return (
+
+
{title}
+
{description}
+
+ )
+}
diff --git a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts
index a85500aadf514..61d98a07a914f 100644
--- a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts
+++ b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.test.ts
@@ -1,10 +1,12 @@
+import { expectLogic, truth } from 'kea-test-utils'
import { dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic'
-import { initKeaTests } from '~/test/init'
+
import { useMocks } from '~/mocks/jest'
+import { dashboardsModel } from '~/models/dashboardsModel'
+import { initKeaTests } from '~/test/init'
import { DashboardType, UserBasicType } from '~/types'
+
import dashboardJson from '../__mocks__/dashboard.json'
-import { dashboardsModel } from '~/models/dashboardsModel'
-import { expectLogic, truth } from 'kea-test-utils'
let dashboardId = 1234
const dashboard = (extras: Partial): DashboardType => {
diff --git a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.ts b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.ts
index 321d7f9a5cd4d..3d9ee6e1969c3 100644
--- a/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.ts
+++ b/frontend/src/scenes/dashboard/dashboards/dashboardsLogic.ts
@@ -1,13 +1,15 @@
-import { actions, connect, kea, path, reducers, selectors } from 'kea'
import Fuse from 'fuse.js'
-import { dashboardsModel } from '~/models/dashboardsModel'
-import type { dashboardsLogicType } from './dashboardsLogicType'
-import { userLogic } from 'scenes/userLogic'
+import { actions, connect, kea, path, reducers, selectors } from 'kea'
import { actionToUrl, router, urlToAction } from 'kea-router'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { objectClean } from 'lib/utils'
+import { userLogic } from 'scenes/userLogic'
+
+import { dashboardsModel } from '~/models/dashboardsModel'
import { DashboardBasicType } from '~/types'
+import type { dashboardsLogicType } from './dashboardsLogicType'
+
export enum DashboardsTab {
Dashboards = 'dashboards',
Templates = 'templates',
diff --git a/frontend/src/scenes/dashboard/dashboards/templates/DashboardTemplatesTable.tsx b/frontend/src/scenes/dashboard/dashboards/templates/DashboardTemplatesTable.tsx
index 471d9da1117a5..d260363e63674 100644
--- a/frontend/src/scenes/dashboard/dashboards/templates/DashboardTemplatesTable.tsx
+++ b/frontend/src/scenes/dashboard/dashboards/templates/DashboardTemplatesTable.tsx
@@ -1,14 +1,15 @@
-import { dashboardTemplatesLogic } from 'scenes/dashboard/dashboards/templates/dashboardTemplatesLogic'
-import { useActions, useValues } from 'kea'
-import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
-import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack'
-import { DashboardTemplateType } from '~/types'
import { LemonButton, LemonDivider } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
import { More } from 'lib/lemon-ui/LemonButton/More'
-import { dashboardTemplateEditorLogic } from 'scenes/dashboard/dashboardTemplateEditorLogic'
+import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
+import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack'
+import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
+import { dashboardTemplatesLogic } from 'scenes/dashboard/dashboards/templates/dashboardTemplatesLogic'
import { DashboardTemplateEditor } from 'scenes/dashboard/DashboardTemplateEditor'
+import { dashboardTemplateEditorLogic } from 'scenes/dashboard/dashboardTemplateEditorLogic'
import { userLogic } from 'scenes/userLogic'
-import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
+
+import { DashboardTemplateType } from '~/types'
export const DashboardTemplatesTable = (): JSX.Element | null => {
const { allTemplates, allTemplatesLoading } = useValues(dashboardTemplatesLogic)
diff --git a/frontend/src/scenes/dashboard/dashboards/templates/dashboardTemplatesLogic.tsx b/frontend/src/scenes/dashboard/dashboards/templates/dashboardTemplatesLogic.tsx
index df662a9b7d1ef..eeceeeb28d23a 100644
--- a/frontend/src/scenes/dashboard/dashboards/templates/dashboardTemplatesLogic.tsx
+++ b/frontend/src/scenes/dashboard/dashboards/templates/dashboardTemplatesLogic.tsx
@@ -1,9 +1,9 @@
import { actions, afterMount, connect, kea, key, path, props } from 'kea'
import { loaders } from 'kea-loaders'
import api from 'lib/api'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { DashboardTemplateScope, DashboardTemplateType } from '~/types'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import type { dashboardTemplatesLogicType } from './dashboardTemplatesLogicType'
diff --git a/frontend/src/scenes/dashboard/deleteDashboardLogic.ts b/frontend/src/scenes/dashboard/deleteDashboardLogic.ts
index d6f59e17acc38..65a0beb798c14 100644
--- a/frontend/src/scenes/dashboard/deleteDashboardLogic.ts
+++ b/frontend/src/scenes/dashboard/deleteDashboardLogic.ts
@@ -1,8 +1,9 @@
import { actions, connect, kea, listeners, path, reducers } from 'kea'
+import { forms } from 'kea-forms'
import { router } from 'kea-router'
import { urls } from 'scenes/urls'
+
import { dashboardsModel } from '~/models/dashboardsModel'
-import { forms } from 'kea-forms'
import type { deleteDashboardLogicType } from './deleteDashboardLogicType'
diff --git a/frontend/src/scenes/dashboard/duplicateDashboardLogic.ts b/frontend/src/scenes/dashboard/duplicateDashboardLogic.ts
index fec8d5a09bb58..108407e9671ab 100644
--- a/frontend/src/scenes/dashboard/duplicateDashboardLogic.ts
+++ b/frontend/src/scenes/dashboard/duplicateDashboardLogic.ts
@@ -1,9 +1,9 @@
import { actions, connect, kea, listeners, path, reducers } from 'kea'
+import { forms } from 'kea-forms'
import { router } from 'kea-router'
import { urls } from 'scenes/urls'
-import { dashboardsModel } from '~/models/dashboardsModel'
-import { forms } from 'kea-forms'
+import { dashboardsModel } from '~/models/dashboardsModel'
import { insightsModel } from '~/models/insightsModel'
import type { duplicateDashboardLogicType } from './duplicateDashboardLogicType'
diff --git a/frontend/src/scenes/dashboard/newDashboardLogic.ts b/frontend/src/scenes/dashboard/newDashboardLogic.ts
index 7c963244ec947..455bfa884cdd7 100644
--- a/frontend/src/scenes/dashboard/newDashboardLogic.ts
+++ b/frontend/src/scenes/dashboard/newDashboardLogic.ts
@@ -1,15 +1,17 @@
import { actions, connect, isBreakpoint, kea, key, listeners, path, props, reducers, selectors } from 'kea'
-import type { newDashboardLogicType } from './newDashboardLogicType'
-import { DashboardRestrictionLevel } from 'lib/constants'
-import { DashboardTemplateType, DashboardType, DashboardTemplateVariableType, DashboardTile, JsonType } from '~/types'
+import { forms } from 'kea-forms'
+import { router } from 'kea-router'
import api from 'lib/api'
+import { DashboardRestrictionLevel } from 'lib/constants'
+import { lemonToast } from 'lib/lemon-ui/lemonToast'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { teamLogic } from 'scenes/teamLogic'
-import { router } from 'kea-router'
import { urls } from 'scenes/urls'
+
import { dashboardsModel } from '~/models/dashboardsModel'
-import { forms } from 'kea-forms'
-import { lemonToast } from 'lib/lemon-ui/lemonToast'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { DashboardTemplateType, DashboardTemplateVariableType, DashboardTile, DashboardType, JsonType } from '~/types'
+
+import type { newDashboardLogicType } from './newDashboardLogicType'
export interface NewDashboardForm {
name: string
diff --git a/frontend/src/scenes/dashboard/tileLayouts.test.ts b/frontend/src/scenes/dashboard/tileLayouts.test.ts
index 63794cf6395b1..20a6d78ae0d24 100644
--- a/frontend/src/scenes/dashboard/tileLayouts.test.ts
+++ b/frontend/src/scenes/dashboard/tileLayouts.test.ts
@@ -1,4 +1,5 @@
import { calculateLayouts } from 'scenes/dashboard/tileLayouts'
+
import { DashboardLayoutSize, DashboardTile, TileLayout } from '~/types'
function tileWithLayout(layouts: Record, tileId: number = 1): DashboardTile {
diff --git a/frontend/src/scenes/dashboard/tileLayouts.ts b/frontend/src/scenes/dashboard/tileLayouts.ts
index 77fdfa268aeeb..90d3481c464c9 100644
--- a/frontend/src/scenes/dashboard/tileLayouts.ts
+++ b/frontend/src/scenes/dashboard/tileLayouts.ts
@@ -1,7 +1,8 @@
import { Layout } from 'react-grid-layout'
-import { ChartDisplayType, DashboardLayoutSize, DashboardTile, FilterType } from '~/types'
-import { isPathsFilter, isRetentionFilter, isTrendsFilter } from 'scenes/insights/sharedUtils'
import { BREAKPOINT_COLUMN_COUNTS, MIN_ITEM_HEIGHT_UNITS, MIN_ITEM_WIDTH_UNITS } from 'scenes/dashboard/dashboardLogic'
+import { isPathsFilter, isRetentionFilter, isTrendsFilter } from 'scenes/insights/sharedUtils'
+
+import { ChartDisplayType, DashboardLayoutSize, DashboardTile, FilterType } from '~/types'
export const sortTilesByLayout = (tiles: Array, col: DashboardLayoutSize): Array => {
return [...tiles].sort((a: DashboardTile, b: DashboardTile) => {
diff --git a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
index 9f2492a913ce4..606c7a97afff5 100644
--- a/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
+++ b/frontend/src/scenes/data-management/DataManagementScene.stories.tsx
@@ -1,15 +1,17 @@
-import { mswDecorator, setFeatureFlags } from '~/mocks/browser'
import { Meta } from '@storybook/react'
-import { useAvailableFeatures } from '~/mocks/features'
-import { AvailableFeature } from '~/types'
-import { useEffect } from 'react'
import { router } from 'kea-router'
-import { urls } from 'scenes/urls'
+import { FEATURE_FLAGS } from 'lib/constants'
+import { dayjs } from 'lib/dayjs'
+import { useEffect } from 'react'
import { App } from 'scenes/App'
+import { urls } from 'scenes/urls'
+
+import { mswDecorator, setFeatureFlags } from '~/mocks/browser'
+import { useAvailableFeatures } from '~/mocks/features'
import { DatabaseSchemaQueryResponse } from '~/queries/schema'
+import { AvailableFeature } from '~/types'
+
import { ingestionWarningsResponse } from './ingestion-warnings/__mocks__/ingestion-warnings-response'
-import { dayjs } from 'lib/dayjs'
-import { FEATURE_FLAGS } from 'lib/constants'
const MOCK_DATABASE: DatabaseSchemaQueryResponse = {
events: [
diff --git a/frontend/src/scenes/data-management/DataManagementScene.tsx b/frontend/src/scenes/data-management/DataManagementScene.tsx
index 5a3bf879ea225..7fec05a325f81 100644
--- a/frontend/src/scenes/data-management/DataManagementScene.tsx
+++ b/frontend/src/scenes/data-management/DataManagementScene.tsx
@@ -1,30 +1,31 @@
import { actions, connect, kea, path, reducers, selectors, useActions, useValues } from 'kea'
import { actionToUrl, urlToAction } from 'kea-router'
-import { urls } from 'scenes/urls'
-import { Tooltip } from 'lib/lemon-ui/Tooltip'
-import { IconInfo } from 'lib/lemon-ui/icons'
+import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog'
+import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity'
+import { PageHeader } from 'lib/components/PageHeader'
import { TitleWithIcon } from 'lib/components/TitleWithIcon'
import { FEATURE_FLAGS } from 'lib/constants'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
-import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
+import { IconInfo } from 'lib/lemon-ui/icons'
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'
import React from 'react'
-import { Scene, SceneExport } from 'scenes/sceneTypes'
-import { PageHeader } from 'lib/components/PageHeader'
import { NewActionButton } from 'scenes/actions/NewActionButton'
import { Annotations } from 'scenes/annotations'
+import { NewAnnotationButton } from 'scenes/annotations/AnnotationModal'
+import { Scene, SceneExport } from 'scenes/sceneTypes'
+import { urls } from 'scenes/urls'
+
+import { Breadcrumb } from '~/types'
+import { ActionsTable } from './actions/ActionsTable'
+import { DatabaseTableList } from './database/DatabaseTableList'
import type { dataManagementSceneLogicType } from './DataManagementSceneType'
-import { NewAnnotationButton } from 'scenes/annotations/AnnotationModal'
import { EventDefinitionsTable } from './events/EventDefinitionsTable'
-import { ActionsTable } from './actions/ActionsTable'
-import { PropertyDefinitionsTable } from './properties/PropertyDefinitionsTable'
-import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog'
-import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity'
import { IngestionWarningsView } from './ingestion-warnings/IngestionWarningsView'
-import { DatabaseTableList } from './database/DatabaseTableList'
-import { Breadcrumb } from '~/types'
-import { capitalizeFirstLetter } from 'lib/utils'
+import { PropertyDefinitionsTable } from './properties/PropertyDefinitionsTable'
export enum DataManagementTab {
Actions = 'actions',
diff --git a/frontend/src/scenes/data-management/actions/ActionsTable.tsx b/frontend/src/scenes/data-management/actions/ActionsTable.tsx
index 3ab88c29bb0be..8efad967b31b3 100644
--- a/frontend/src/scenes/data-management/actions/ActionsTable.tsx
+++ b/frontend/src/scenes/data-management/actions/ActionsTable.tsx
@@ -1,27 +1,29 @@
+import { LemonInput, LemonSegmentedButton } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { combineUrl } from 'kea-router'
+import api from 'lib/api'
+import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
+import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
+import { IconCheckmark, IconPlayCircle } from 'lib/lemon-ui/icons'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { More } from 'lib/lemon-ui/LemonButton/More'
+import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
+import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown'
+import { LemonTable } from 'lib/lemon-ui/LemonTable'
+import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
+import { LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable/types'
import { Link } from 'lib/lemon-ui/Link'
import { stripHTTP } from 'lib/utils'
import { deleteWithUndo } from 'lib/utils/deleteWithUndo'
-import { useActions, useValues } from 'kea'
+import { actionsLogic } from 'scenes/actions/actionsLogic'
+import { userLogic } from 'scenes/userLogic'
+
import { actionsModel } from '~/models/actionsModel'
-import { NewActionButton } from '../../actions/NewActionButton'
import { ActionType, AvailableFeature, ChartDisplayType, InsightType, ProductKey } from '~/types'
-import { userLogic } from 'scenes/userLogic'
+
+import { NewActionButton } from '../../actions/NewActionButton'
import { teamLogic } from '../../teamLogic'
-import api from 'lib/api'
import { urls } from '../../urls'
-import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils'
-import { LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable/types'
-import { LemonTable } from 'lib/lemon-ui/LemonTable'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
-import { More } from 'lib/lemon-ui/LemonButton/More'
-import { combineUrl } from 'kea-router'
-import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
-import { LemonInput, LemonSegmentedButton } from '@posthog/lemon-ui'
-import { actionsLogic } from 'scenes/actions/actionsLogic'
-import { IconCheckmark, IconPlayCircle } from 'lib/lemon-ui/icons'
-import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction'
-import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown'
export function ActionsTable(): JSX.Element {
const { currentTeam } = useValues(teamLogic)
diff --git a/frontend/src/scenes/data-management/dataManagementDescribers.tsx b/frontend/src/scenes/data-management/dataManagementDescribers.tsx
index 40a883bb7f44b..c1c2672619fdd 100644
--- a/frontend/src/scenes/data-management/dataManagementDescribers.tsx
+++ b/frontend/src/scenes/data-management/dataManagementDescribers.tsx
@@ -8,10 +8,10 @@ import {
HumanizedChange,
} from 'lib/components/ActivityLog/humanizeActivity'
import { SentenceList } from 'lib/components/ActivityLog/SentenceList'
-import { pluralize } from 'lib/utils'
import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
import { IconVerifiedEvent } from 'lib/lemon-ui/icons'
import { Link } from 'lib/lemon-ui/Link'
+import { pluralize } from 'lib/utils'
import { urls } from 'scenes/urls'
const dataManagementActionsMapping: Record<
diff --git a/frontend/src/scenes/data-management/database/DatabaseTable.tsx b/frontend/src/scenes/data-management/database/DatabaseTable.tsx
index 1f330f8ad7f2d..bbf5c67db647e 100644
--- a/frontend/src/scenes/data-management/database/DatabaseTable.tsx
+++ b/frontend/src/scenes/data-management/database/DatabaseTable.tsx
@@ -1,8 +1,8 @@
import { LemonTable } from 'lib/lemon-ui/LemonTable'
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
import { Link } from 'lib/lemon-ui/Link'
-import { ViewLinkDeleteButton } from 'scenes/data-warehouse/ViewLinkModal'
import { DatabaseTableListRow } from 'scenes/data-warehouse/types'
+import { ViewLinkDeleteButton } from 'scenes/data-warehouse/ViewLinkModal'
import { urls } from 'scenes/urls'
interface DatabaseTableProps {
diff --git a/frontend/src/scenes/data-management/database/DatabaseTableList.tsx b/frontend/src/scenes/data-management/database/DatabaseTableList.tsx
index e9d41917e7031..d6e9763ec5cc3 100644
--- a/frontend/src/scenes/data-management/database/DatabaseTableList.tsx
+++ b/frontend/src/scenes/data-management/database/DatabaseTableList.tsx
@@ -1,8 +1,9 @@
-import { databaseTableListLogic } from './databaseTableListLogic'
-import { useActions, useValues } from 'kea'
import { LemonInput, Link } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
import { DatabaseTablesContainer } from 'scenes/data-management/database/DatabaseTables'
+import { databaseTableListLogic } from './databaseTableListLogic'
+
export function DatabaseTableList(): JSX.Element {
const { searchTerm } = useValues(databaseTableListLogic)
const { setSearchTerm } = useActions(databaseTableListLogic)
diff --git a/frontend/src/scenes/data-management/database/DatabaseTables.tsx b/frontend/src/scenes/data-management/database/DatabaseTables.tsx
index 2bad7c2909802..003d918cb7e07 100644
--- a/frontend/src/scenes/data-management/database/DatabaseTables.tsx
+++ b/frontend/src/scenes/data-management/database/DatabaseTables.tsx
@@ -1,15 +1,17 @@
-import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
+import { LemonButton, Link } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
-import { databaseTableListLogic, DatabaseTableListRow } from 'scenes/data-management/database/databaseTableListLogic'
+import { FEATURE_FLAGS } from 'lib/constants'
+import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable'
import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag'
-import { LemonButton, Link } from '@posthog/lemon-ui'
+import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
+import { databaseTableListLogic, DatabaseTableListRow } from 'scenes/data-management/database/databaseTableListLogic'
+import { viewLinkLogic } from 'scenes/data-warehouse/viewLinkLogic'
+import { ViewLinkModal } from 'scenes/data-warehouse/ViewLinkModal'
import { urls } from 'scenes/urls'
+
import { DataTableNode, NodeKind } from '~/queries/schema'
+
import { DatabaseTable } from './DatabaseTable'
-import { viewLinkLogic } from 'scenes/data-warehouse/viewLinkLogic'
-import { ViewLinkModal } from 'scenes/data-warehouse/ViewLinkModal'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
-import { FEATURE_FLAGS } from 'lib/constants'
export function DatabaseTablesContainer(): JSX.Element {
const { filteredTables, databaseLoading } = useValues(databaseTableListLogic)
diff --git a/frontend/src/scenes/data-management/database/databaseTableListLogic.ts b/frontend/src/scenes/data-management/database/databaseTableListLogic.ts
index dcacee040b848..7fac0498924d4 100644
--- a/frontend/src/scenes/data-management/database/databaseTableListLogic.ts
+++ b/frontend/src/scenes/data-management/database/databaseTableListLogic.ts
@@ -1,7 +1,7 @@
import { actions, afterMount, kea, path, reducers, selectors } from 'kea'
import { loaders } from 'kea-loaders'
-import { query } from '~/queries/query'
+import { query } from '~/queries/query'
import { DatabaseSchemaQuery, DatabaseSchemaQueryResponseField, NodeKind } from '~/queries/schema'
import type { databaseTableListLogicType } from './databaseTableListLogicType'
diff --git a/frontend/src/scenes/data-management/definition/DefinitionEdit.tsx b/frontend/src/scenes/data-management/definition/DefinitionEdit.tsx
index b6609ee75ddf1..03c9848aae356 100644
--- a/frontend/src/scenes/data-management/definition/DefinitionEdit.tsx
+++ b/frontend/src/scenes/data-management/definition/DefinitionEdit.tsx
@@ -1,17 +1,18 @@
-import { PageHeader } from 'lib/components/PageHeader'
-import { DefinitionPageMode } from 'scenes/data-management/definition/definitionLogic'
import { useActions, useValues } from 'kea'
-import { definitionEditLogic, DefinitionEditLogicProps } from 'scenes/data-management/definition/definitionEditLogic'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { Form } from 'kea-forms'
+import { VerifiedDefinitionCheckbox } from 'lib/components/DefinitionPopover/DefinitionPopoverContents'
+import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
+import { PageHeader } from 'lib/components/PageHeader'
import { Field } from 'lib/forms/Field'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
+import { LemonSelect } from 'lib/lemon-ui/LemonSelect'
import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea'
-import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
import { getPropertyLabel, isPostHogProp } from 'lib/taxonomy'
-import { VerifiedDefinitionCheckbox } from 'lib/components/DefinitionPopover/DefinitionPopoverContents'
-import { LemonSelect } from 'lib/lemon-ui/LemonSelect'
-import { Form } from 'kea-forms'
+import { definitionEditLogic, DefinitionEditLogicProps } from 'scenes/data-management/definition/definitionEditLogic'
+import { DefinitionPageMode } from 'scenes/data-management/definition/definitionLogic'
+
import { tagsModel } from '~/models/tagsModel'
-import { LemonDivider } from 'lib/lemon-ui/LemonDivider'
export function DefinitionEdit(props: DefinitionEditLogicProps): JSX.Element {
const logic = definitionEditLogic(props)
diff --git a/frontend/src/scenes/data-management/definition/DefinitionView.tsx b/frontend/src/scenes/data-management/definition/DefinitionView.tsx
index bf3ef6a9abb07..400da13b5b2d6 100644
--- a/frontend/src/scenes/data-management/definition/DefinitionView.tsx
+++ b/frontend/src/scenes/data-management/definition/DefinitionView.tsx
@@ -1,33 +1,35 @@
import './Definition.scss'
+
+import { TZLabel } from '@posthog/apps-common'
+import { LemonDivider } from '@posthog/lemon-ui'
import clsx from 'clsx'
-import { Divider } from 'antd'
-import { SceneExport } from 'scenes/sceneTypes'
-import { PageHeader } from 'lib/components/PageHeader'
-import { EditableField } from 'lib/components/EditableField/EditableField'
-import { AvailableFeature, PropertyDefinition } from '~/types'
-import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
import { useActions, useValues } from 'kea'
-import { userLogic } from 'scenes/userLogic'
+import { combineUrl } from 'kea-router/lib/utils'
import { DefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopover'
+import { EditableField } from 'lib/components/EditableField/EditableField'
+import { NotFound } from 'lib/components/NotFound'
+import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags'
+import { PageHeader } from 'lib/components/PageHeader'
+import { IconPlayCircle } from 'lib/lemon-ui/icons'
+import { LemonButton } from 'lib/lemon-ui/LemonButton'
+import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
+import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner'
+import { getPropertyLabel } from 'lib/taxonomy'
+import { DefinitionEdit } from 'scenes/data-management/definition/DefinitionEdit'
import {
definitionLogic,
DefinitionLogicProps,
DefinitionPageMode,
} from 'scenes/data-management/definition/definitionLogic'
-import { LemonButton } from 'lib/lemon-ui/LemonButton'
-import { DefinitionEdit } from 'scenes/data-management/definition/DefinitionEdit'
import { EventDefinitionProperties } from 'scenes/data-management/events/EventDefinitionProperties'
-import { getPropertyLabel } from 'lib/taxonomy'
-import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner'
-import { NotFound } from 'lib/components/NotFound'
-import { IconPlayCircle } from 'lib/lemon-ui/icons'
-import { combineUrl } from 'kea-router/lib/utils'
+import { SceneExport } from 'scenes/sceneTypes'
import { urls } from 'scenes/urls'
-import { NodeKind } from '~/queries/schema'
-import { Query } from '~/queries/Query/Query'
+import { userLogic } from 'scenes/userLogic'
+
import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils'
-import { LemonDialog } from 'lib/lemon-ui/LemonDialog'
-import { TZLabel } from '@posthog/apps-common'
+import { Query } from '~/queries/Query/Query'
+import { NodeKind } from '~/queries/schema'
+import { AvailableFeature, PropertyDefinition } from '~/types'
export const scene: SceneExport = {
component: DefinitionView,
@@ -192,7 +194,7 @@ export function DefinitionView(props: DefinitionLogicProps = {}): JSX.Element {
>
}
/>
-
+
{isEvent && (
<>
@@ -214,11 +216,11 @@ export function DefinitionView(props: DefinitionLogicProps = {}): JSX.Element {
/>
)}
-
+
{isEvent && definition.id !== 'new' && (
<>
-
+
Matching events
diff --git a/frontend/src/scenes/data-management/definition/definitionEditLogic.test.ts b/frontend/src/scenes/data-management/definition/definitionEditLogic.test.ts
index 1f43f33f8113b..4399c959fd98f 100644
--- a/frontend/src/scenes/data-management/definition/definitionEditLogic.test.ts
+++ b/frontend/src/scenes/data-management/definition/definitionEditLogic.test.ts
@@ -1,14 +1,15 @@
-import { definitionLogic } from 'scenes/data-management/definition/definitionLogic'
-import { useMocks } from '~/mocks/jest'
-import { mockEventDefinitions, mockEventPropertyDefinition } from '~/test/mocks'
-import { initKeaTests } from '~/test/init'
-import { definitionEditLogic } from 'scenes/data-management/definition/definitionEditLogic'
+import { router } from 'kea-router'
import { expectLogic } from 'kea-test-utils'
+import { definitionEditLogic } from 'scenes/data-management/definition/definitionEditLogic'
+import { definitionLogic } from 'scenes/data-management/definition/definitionLogic'
import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic'
import { propertyDefinitionsTableLogic } from 'scenes/data-management/properties/propertyDefinitionsTableLogic'
-import { router } from 'kea-router'
import { urls } from 'scenes/urls'
+import { useMocks } from '~/mocks/jest'
+import { initKeaTests } from '~/test/init'
+import { mockEventDefinitions, mockEventPropertyDefinition } from '~/test/mocks'
+
describe('definitionEditLogic', () => {
let logic: ReturnType