diff --git a/.eslintrc.js b/.eslintrc.js index 91fe2aed3b1e8..5f71d84aebdd8 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,13 +42,25 @@ module.exports = { }, ecmaVersion: 2018, sourceType: 'module', - project: 'tsconfig.json' + project: 'tsconfig.json', }, - plugins: ['prettier', 'react', 'cypress', '@typescript-eslint', 'no-only-tests', 'jest', 'compat', 'posthog'], + plugins: [ + 'prettier', + 'react', + 'cypress', + '@typescript-eslint', + 'no-only-tests', + 'jest', + 'compat', + 'posthog', + 'simple-import-sort', + ], rules: { 'no-console': ['error', { allow: ['warn', 'error'] }], 'no-debugger': 'error', 'no-only-tests/no-only-tests': 'error', + 'simple-import-sort/imports': 'error', + 'simple-import-sort/exports': 'error', 'react/prop-types': [0], 'react/react-in-jsx-scope': [0], 'react/no-unescaped-entities': [0], @@ -112,6 +124,11 @@ module.exports = { importNames: ['Tooltip'], message: 'Please use Tooltip from @posthog/lemon-ui instead.', }, + { + name: 'antd', + importNames: ['Alert'], + message: 'Please use LemonBanner from @posthog/lemon-ui instead.', + }, ], }, ], @@ -231,6 +248,10 @@ module.exports = { element: 'a', message: 'use instead', }, + { + element: 'Alert', + message: 'use instead', + }, ], }, ], @@ -254,14 +275,22 @@ module.exports = { rules: { // The below complains needlessly about expect(api.createInvite).toHaveBeenCalledWith(...) '@typescript-eslint/unbound-method': 'off', - } + }, }, { - // disable these rules for files generated by kea-typegen - files: ['*Type.ts', '*Type.tsx'], + files: ['*Type.ts', '*Type.tsx'], // Kea typegen output rules: { 'no-restricted-imports': 'off', - '@typescript-eslint/ban-types': ['off'], + '@typescript-eslint/ban-types': 'off', + 'simple-import-sort/imports': 'off', + 'simple-import-sort/exports': 'off', + }, + }, + { + files: ['frontend/src/scenes/notebooks/Nodes/*'], // Notebooks code weirdly relies on its order of sorting + rules: { + 'simple-import-sort/imports': 'off', + 'simple-import-sort/exports': 'off', }, }, { diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index d290a0594bbf0..2ea70218bd608 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -2,10 +2,6 @@ name: Frontend CI on: pull_request: - # NOTE: by running on master, aside from highlight issues on master it also - # ensures we have e.g. node modules cached for master, which can then be - # used for branches. See https://github.com/actions/cache#cache-scopes for - # scope details. push: branches: - master @@ -15,28 +11,71 @@ concurrency: cancel-in-progress: true jobs: + # Job to decide if we should run frontend ci + # See https://github.com/dorny/paths-filter#conditional-execution for more details + # we skip each step individually, so they are still reported as success + # because many of them are required for CI checks to be green + changes: + runs-on: ubuntu-latest + timeout-minutes: 5 + name: Determine need to run frontend checks + outputs: + frontend: ${{ steps.filter.outputs.frontend }} + steps: + # For pull requests it's not necessary to check out the code, but we + # also want this to run on master, so we need to check out + - uses: actions/checkout@v3 + + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + frontend: + # Avoid running frontend tests for irrelevant changes + # NOTE: we are at risk of missing a dependency here. + - 'bin/**' + - 'frontend/**' + # Make sure we run if someone is explicitly change the workflow + - .github/workflows/ci-frontend.yml + # various JS config files + - .eslintrc.js + - .prettier* + - babel.config.js + - jest.*.ts + - tsconfig.json + - tsconfig.*.json + - webpack.config.js + - postcss.config.js + - stylelint* + frontend-code-quality: name: Code quality checks + needs: changes # kea typegen and typescript:check need some more oomph runs-on: ubuntu-latest steps: + # we need at least one thing to run to make sure we include everything for required jobs - uses: actions/checkout@v3 - name: Install pnpm + if: needs.changes.outputs.frontend == 'true' uses: pnpm/action-setup@v2 with: version: 8.x.x - name: Set up Node.js - uses: buildjet/setup-node@v3 + if: needs.changes.outputs.frontend == 'true' + uses: actions/setup-node@v3 with: node-version: 18 - name: Get pnpm cache directory path + if: needs.changes.outputs.frontend == 'true' id: pnpm-cache-dir run: echo "PNPM_STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT - uses: actions/cache@v3 + if: needs.changes.outputs.frontend == 'true' id: pnpm-cache with: path: ${{ steps.pnpm-cache-dir.outputs.PNPM_STORE_PATH }} @@ -44,24 +83,31 @@ jobs: restore-keys: ${{ runner.os }}-pnpm-cypress- - name: Install package.json dependencies with pnpm + if: needs.changes.outputs.frontend == 'true' run: pnpm install --frozen-lockfile - name: Check formatting with prettier + if: needs.changes.outputs.frontend == 'true' run: pnpm prettier:check - name: Lint with Stylelint + if: needs.changes.outputs.frontend == 'true' run: pnpm lint:css - name: Generate logic types and run typescript with strict + if: needs.changes.outputs.frontend == 'true' run: pnpm typegen:write && pnpm typescript:check - name: Lint with ESLint + if: needs.changes.outputs.frontend == 'true' run: pnpm lint:js - name: Check if "schema.json" is up to date + if: needs.changes.outputs.frontend == 'true' run: pnpm schema:build:json && git diff --exit-code - name: Check toolbar bundle size + if: needs.changes.outputs.frontend == 'true' uses: preactjs/compressed-size-action@v2 with: build-script: 'build' @@ -70,6 +116,7 @@ jobs: jest: runs-on: ubuntu-latest + needs: changes name: Jest test (${{ matrix.chunk }}) strategy: @@ -79,24 +126,29 @@ jobs: chunk: [1, 2, 3] steps: + # we need at least one thing to run to make sure we include everything for required jobs - uses: actions/checkout@v3 - name: Install pnpm + if: needs.changes.outputs.frontend == 'true' uses: pnpm/action-setup@v2 with: version: 8.x.x - name: Set up Node.js - uses: buildjet/setup-node@v3 + if: needs.changes.outputs.frontend == 'true' + uses: actions/setup-node@v3 with: node-version: 18 cache: pnpm - name: Install package.json dependencies with pnpm + if: needs.changes.outputs.frontend == 'true' run: pnpm install --frozen-lockfile - name: Test with Jest # set maxWorkers or Jest only uses 1 CPU in GitHub Actions run: pnpm test:unit --maxWorkers=2 --shard=${{ matrix.chunk }}/3 + if: needs.changes.outputs.frontend == 'true' env: NODE_OPTIONS: --max-old-space-size=6144 diff --git a/.github/workflows/storybook-chromatic.yml b/.github/workflows/storybook-chromatic.yml index 02397ca2586db..9ae51e2933067 100644 --- a/.github/workflows/storybook-chromatic.yml +++ b/.github/workflows/storybook-chromatic.yml @@ -144,6 +144,7 @@ jobs: HOME: /root # Update snapshots for PRs on the main repo, verify on forks, which don't have access to PostHog Bot VARIANT: ${{ github.event.pull_request.head.repo.full_name == github.repository && 'update' || 'verify' }} + STORYBOOK_SKIP_TAGS: 'test-skip,test-skip-${{ matrix.browser }}' run: | pnpm test:visual-regression:stories:ci:$VARIANT --browsers ${{ matrix.browser }} --shard ${{ matrix.shard }}/$SHARD_COUNT diff --git a/.storybook/decorators/withSnapshotsDisabled.tsx b/.storybook/decorators/withSnapshotsDisabled.tsx deleted file mode 100644 index 6e7598c7a9c7e..0000000000000 --- a/.storybook/decorators/withSnapshotsDisabled.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Decorator } from '@storybook/react' -import { inStorybookTestRunner } from 'lib/utils' - -/** Workaround for https://github.com/storybookjs/test-runner/issues/74 */ -// TODO: Smoke-test all the stories by removing this decorator, once all the stories pass -export const withSnapshotsDisabled: Decorator = (Story, { parameters }) => { - if (parameters?.testOptions?.skip && inStorybookTestRunner()) { - return <>Disabled for Test Runner - } - return -} diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 091884046929e..fe45ae823c620 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -8,7 +8,6 @@ import { getStorybookAppContext } from './app-context' import { withKea } from './decorators/withKea' import { withMockDate } from './decorators/withMockDate' import { defaultMocks } from '~/mocks/handlers' -import { withSnapshotsDisabled } from './decorators/withSnapshotsDisabled' import { withFeatureFlags } from './decorators/withFeatureFlags' import { withTheme } from './decorators/withTheme' @@ -79,7 +78,6 @@ export const parameters: Parameters = { // Setup storybook global decorators. See https://storybook.js.org/docs/react/writing-stories/decorators#global-decorators export const decorators: Meta['decorators'] = [ - withSnapshotsDisabled, // Make sure the msw service worker is started, and reset the handlers to defaults. withKea, // Allow us to time travel to ensure our stories don't change over time. diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index 8decd13e17a65..0ec91135cda24 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -13,11 +13,6 @@ declare module '@storybook/types' { options?: any layout?: 'padded' | 'fullscreen' | 'centered' testOptions?: { - /** - * Whether the test should be a no-op (doesn't jest.skip as @storybook/test-runner doesn't allow that). - * @default false - */ - skip?: boolean /** * Whether we should wait for all loading indicators to disappear before taking a snapshot. * @default true @@ -71,19 +66,20 @@ module.exports = { jest.retryTimes(RETRY_TIMES, { logErrorsBeforeRetry: true }) jest.setTimeout(JEST_TIMEOUT_MS) }, - async postRender(page, context) { + async postVisit(page, context) { const browserContext = page.context() const storyContext = (await getStoryContext(page, context)) as StoryContext - const { skip = false, snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {} + const { snapshotBrowsers = ['chromium'] } = storyContext.parameters?.testOptions ?? {} browserContext.setDefaultTimeout(PLAYWRIGHT_TIMEOUT_MS) - if (!skip) { - const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName - if (snapshotBrowsers.includes(currentBrowser)) { - await expectStoryToMatchSnapshot(page, context, storyContext, currentBrowser) - } + const currentBrowser = browserContext.browser()!.browserType().name() as SupportedBrowserName + if (snapshotBrowsers.includes(currentBrowser)) { + await expectStoryToMatchSnapshot(page, context, storyContext, currentBrowser) } }, + tags: { + skip: ['test-skip'], // NOTE: This is overridden by the CI action storybook-chromatic.yml to include browser specific skipping + }, } as TestRunnerConfig async function expectStoryToMatchSnapshot( diff --git a/.stylelintrc.js b/.stylelintrc.js index 5df820f88d8fb..19bbc999f0f51 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -10,6 +10,7 @@ module.exports = { // CSS Color Module Level 3 says currentColor, Level 4 candidate says currentcolor // Sticking to Level 3 for now camelCaseSvgKeywords: true, + ignoreKeywords: ['BlinkMacSystemFont'], // BlinkMacSystemFont MUST have this particular casing }, ], // Sadly Safari only started supporting the range syntax of media queries in 2023, so let's switch to that diff --git a/cypress/e2e/featureFlags.cy.ts b/cypress/e2e/featureFlags.cy.ts index 9045a51a4f485..bf37822321ad1 100644 --- a/cypress/e2e/featureFlags.cy.ts +++ b/cypress/e2e/featureFlags.cy.ts @@ -48,6 +48,7 @@ describe('Feature Flags', () => { cy.get('[data-attr=save-feature-flag]').first().click() // after save there should be a delete button + cy.get('[data-attr="more-button"]').click() cy.get('button[data-attr="delete-feature-flag"]').should('have.text', 'Delete feature flag') // make sure the data is there as expected after a page reload! @@ -83,11 +84,13 @@ describe('Feature Flags', () => { cy.get('[data-attr=save-feature-flag]').first().click() // after save there should be a delete button + cy.get('[data-attr="more-button"]').click() cy.get('button[data-attr="delete-feature-flag"]').should('have.text', 'Delete feature flag') cy.clickNavMenu('featureflags') cy.get('[data-attr=feature-flag-table]').should('contain', name) cy.get(`[data-row-key=${name}]`).contains(name).click() + cy.get('[data-attr="more-button"]').click() cy.get('[data-attr=delete-feature-flag]').click() cy.get('.Toastify').contains('Undo').should('be.visible') }) diff --git a/cypress/e2e/insights.cy.ts b/cypress/e2e/insights.cy.ts index 5157d21429ba9..80b1c06c4398e 100644 --- a/cypress/e2e/insights.cy.ts +++ b/cypress/e2e/insights.cy.ts @@ -1,7 +1,8 @@ import { urls } from 'scenes/urls' -import { randomString } from '../support/random' + import { decideResponse } from '../fixtures/api/decide' -import { savedInsights, createInsight, insight } from '../productAnalytics' +import { createInsight, insight, savedInsights } from '../productAnalytics' +import { randomString } from '../support/random' // For tests related to trends please check trendsElements.js // insight tests were split up because Cypress was struggling with this many tests in one file🙈 diff --git a/ee/api/feature_flag_role_access.py b/ee/api/feature_flag_role_access.py index d3ca7a68c1a32..3ce77dca89599 100644 --- a/ee/api/feature_flag_role_access.py +++ b/ee/api/feature_flag_role_access.py @@ -35,7 +35,11 @@ def has_permission(self, request, view): return True try: feature_flag: FeatureFlag = FeatureFlag.objects.get(id=view.parents_query_dict["feature_flag_id"]) - if feature_flag.created_by.uuid == request.user.uuid: + if ( + hasattr(feature_flag, "created_by") + and feature_flag.created_by + and feature_flag.created_by.uuid == request.user.uuid + ): return True except FeatureFlag.DoesNotExist: raise exceptions.NotFound("Feature flag not found.") diff --git a/ee/api/test/test_billing.py b/ee/api/test/test_billing.py index 88addd2d7f416..c37c3ee9d6482 100644 --- a/ee/api/test/test_billing.py +++ b/ee/api/test/test_billing.py @@ -2,9 +2,9 @@ from typing import Any, Dict, List from unittest.mock import MagicMock, patch from uuid import uuid4 +from zoneinfo import ZoneInfo import jwt -from zoneinfo import ZoneInfo from dateutil.relativedelta import relativedelta from django.utils.timezone import now from freezegun import freeze_time diff --git a/ee/api/test/test_feature_flag_role_access.py b/ee/api/test/test_feature_flag_role_access.py index f143f10505f0f..3cd4e947d90c9 100644 --- a/ee/api/test/test_feature_flag_role_access.py +++ b/ee/api/test/test_feature_flag_role_access.py @@ -37,6 +37,26 @@ def test_can_always_add_role_access_if_creator_of_feature_flag(self): self.assertEqual(flag_role.role.name, self.eng_role.name) self.assertEqual(flag_role.feature_flag.id, self.feature_flag.id) + def test_role_access_with_deleted_creator_of_feature_flag(self): + OrganizationResourceAccess.objects.create( + resource=OrganizationResourceAccess.Resources.FEATURE_FLAGS, + access_level=OrganizationResourceAccess.AccessLevel.CAN_ONLY_VIEW, + organization=self.organization, + ) + + flag = FeatureFlag.objects.create( + created_by=None, + team=self.team, + key="flag_role_access_none", + name="Flag role access", + ) + self.assertEqual(self.user.role_memberships.count(), 0) + flag_role_access_create_res = self.client.post( + f"/api/projects/@current/feature_flags/{flag.id}/role_access", + {"role_id": self.eng_role.id}, + ) + self.assertEqual(flag_role_access_create_res.status_code, status.HTTP_403_FORBIDDEN) + def test_cannot_add_role_access_if_feature_flags_access_level_too_low_and_not_creator(self): OrganizationResourceAccess.objects.create( resource=OrganizationResourceAccess.Resources.FEATURE_FLAGS, diff --git a/ee/api/test/test_organization.py b/ee/api/test/test_organization.py index 2f1b11bb95256..a77361dc579e8 100644 --- a/ee/api/test/test_organization.py +++ b/ee/api/test/test_organization.py @@ -1,6 +1,7 @@ import datetime as dt import random -from unittest.mock import ANY, patch +from unittest import mock +from unittest.mock import ANY, call, patch from freezegun.api import freeze_time from rest_framework import status @@ -104,11 +105,21 @@ def test_delete_last_organization(self, mock_capture): "Did not return a 404 on trying to delete a nonexistent org", ) - mock_capture.assert_called_once_with( - self.user.distinct_id, - "organization deleted", - organization_props, - groups={"instance": ANY, "organization": str(org_id)}, + mock_capture.assert_has_calls( + [ + call( + self.user.distinct_id, + "membership level changed", + properties={"new_level": 15, "previous_level": 1}, + groups=mock.ANY, + ), + call( + self.user.distinct_id, + "organization deleted", + organization_props, + groups={"instance": mock.ANY, "organization": str(org_id)}, + ), + ] ) def test_no_delete_organization_not_owning(self): diff --git a/ee/billing/billing_manager.py b/ee/billing/billing_manager.py index 5a8119c57df9b..324b158fe071d 100644 --- a/ee/billing/billing_manager.py +++ b/ee/billing/billing_manager.py @@ -6,6 +6,7 @@ import structlog from django.utils import timezone from rest_framework.exceptions import NotAuthenticated +from sentry_sdk import capture_exception from ee.billing.billing_types import BillingStatus from ee.billing.quota_limiting import set_org_usage_summary, sync_org_quota_limits @@ -13,7 +14,7 @@ from ee.settings import BILLING_SERVICE_URL from posthog.cloud_utils import get_cached_instance_license from posthog.models import Organization -from posthog.models.organization import OrganizationUsageInfo +from posthog.models.organization import OrganizationMembership, OrganizationUsageInfo logger = structlog.get_logger(__name__) @@ -114,6 +115,14 @@ def update_billing_distinct_ids(self, organization: Organization) -> None: distinct_ids = list(organization.members.values_list("distinct_id", flat=True)) self.update_billing(organization, {"distinct_ids": distinct_ids}) + def update_billing_customer_email(self, organization: Organization) -> None: + try: + owner_membership = OrganizationMembership.objects.get(organization=organization, level=15) + user = owner_membership.user + self.update_billing(organization, {"org_customer_email": user.email}) + except Exception as e: + capture_exception(e) + def deactivate_products(self, organization: Organization, products: str) -> None: res = requests.get( f"{BILLING_SERVICE_URL}/api/billing/deactivate?products={products}", diff --git a/ee/billing/test/test_billing_manager.py b/ee/billing/test/test_billing_manager.py index e0c09e0d071fb..1dbbcb464f068 100644 --- a/ee/billing/test/test_billing_manager.py +++ b/ee/billing/test/test_billing_manager.py @@ -33,3 +33,26 @@ def test_update_billing_distinct_ids(self, billing_patch_request_mock: MagicMock BillingManager(license).update_billing_distinct_ids(organization) assert billing_patch_request_mock.call_count == 1 assert len(billing_patch_request_mock.call_args[1]["json"]["distinct_ids"]) == 2 + + @patch( + "ee.billing.billing_manager.requests.patch", + return_value=MagicMock(status_code=200, json=MagicMock(return_value={"text": "ok"})), + ) + def test_update_billing_customer_email(self, billing_patch_request_mock: MagicMock): + organization = self.organization + license = super(LicenseManager, cast(LicenseManager, License.objects)).create( + key="key123::key123", + plan="enterprise", + valid_until=timezone.datetime(2038, 1, 19, 3, 14, 7), + ) + User.objects.create_and_join( + organization=organization, + email="y@x.com", + password=None, + level=OrganizationMembership.Level.OWNER, + ) + organization.refresh_from_db() + assert len(organization.members.values_list("distinct_id", flat=True)) == 2 # one exists in the test base + BillingManager(license).update_billing_customer_email(organization) + assert billing_patch_request_mock.call_count == 1 + assert billing_patch_request_mock.call_args[1]["json"]["org_customer_email"] == "y@x.com" diff --git a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr index 262c4a8e1e195..3474ae77b858f 100644 --- a/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr +++ b/ee/clickhouse/views/test/__snapshots__/test_clickhouse_experiment_secondary_results.ambr @@ -1,6 +1,6 @@ # name: ClickhouseTestExperimentSecondaryResults.test_basic_secondary_metric_results ' - /* user_id:132 celery:posthog.celery.sync_insight_caching_state */ + /* user_id:131 celery:posthog.celery.sync_insight_caching_state */ SELECT team_id, date_diff('second', max(timestamp), now()) AS age FROM events diff --git a/frontend/__snapshots__/components-command-bar--actions.png b/frontend/__snapshots__/components-command-bar--actions.png new file mode 100644 index 0000000000000..a1c3b448c6a2e Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--actions.png differ diff --git a/frontend/__snapshots__/components-command-bar--search.png b/frontend/__snapshots__/components-command-bar--search.png new file mode 100644 index 0000000000000..d155d92fac149 Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--search.png differ diff --git a/frontend/__snapshots__/components-command-bar--shortcuts.png b/frontend/__snapshots__/components-command-bar--shortcuts.png new file mode 100644 index 0000000000000..32f4e7ff34955 Binary files /dev/null and b/frontend/__snapshots__/components-command-bar--shortcuts.png differ diff --git a/frontend/__snapshots__/components-compact-list--compact-list.png b/frontend/__snapshots__/components-compact-list--compact-list.png index 4a4b5e8704410..49cbfb2048571 100644 Binary files a/frontend/__snapshots__/components-compact-list--compact-list.png and b/frontend/__snapshots__/components-compact-list--compact-list.png differ diff --git a/frontend/__snapshots__/components-networkrequesttiming--basic.png b/frontend/__snapshots__/components-networkrequesttiming--basic.png index 8247b433f71c8..effc91c21a0a6 100644 Binary files a/frontend/__snapshots__/components-networkrequesttiming--basic.png and b/frontend/__snapshots__/components-networkrequesttiming--basic.png differ diff --git a/frontend/__snapshots__/exporter-exporter--trends-number-insight.png b/frontend/__snapshots__/exporter-exporter--trends-number-insight.png index e661eaa4de199..67b2a70ddeb75 100644 Binary files a/frontend/__snapshots__/exporter-exporter--trends-number-insight.png and b/frontend/__snapshots__/exporter-exporter--trends-number-insight.png differ diff --git a/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png b/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png index b49b6fc4bd341..b2ae49cd91f5b 100644 Binary files a/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png and b/frontend/__snapshots__/layout-navigation--app-page-with-side-bar-shown.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png b/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png index a05dd78b3e3e7..a7a8ac55c5061 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--closable.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png index be2ef2e5a884b..540a8a3ef2c39 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--dismissable.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--error.png b/frontend/__snapshots__/lemon-ui-lemon-banner--error.png index 7db8c557495b9..9389cfa4ea1b2 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--error.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--error.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--info.png b/frontend/__snapshots__/lemon-ui-lemon-banner--info.png index 7c6e78d57caf2..6848c05f89a32 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--info.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--info.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--success.png b/frontend/__snapshots__/lemon-ui-lemon-banner--success.png index 2053ce5ccc6de..f3b58cb98363a 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--success.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--success.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png b/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png index bf8c975d7385b..3c41933fb5078 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png and b/frontend/__snapshots__/lemon-ui-lemon-banner--warning.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png b/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png index 24ae6fe59d181..292f9ce7d0a99 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png and b/frontend/__snapshots__/lemon-ui-lemon-button--as-links.png differ diff --git a/frontend/__snapshots__/lemon-ui-textfit--basic.png b/frontend/__snapshots__/lemon-ui-textfit--basic.png new file mode 100644 index 0000000000000..269bfc9cfad86 Binary files /dev/null and b/frontend/__snapshots__/lemon-ui-textfit--basic.png differ diff --git a/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png new file mode 100644 index 0000000000000..995c1bc753ad1 Binary files /dev/null and b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--default.png differ diff --git a/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png new file mode 100644 index 0000000000000..995c1bc753ad1 Binary files /dev/null and b/frontend/__snapshots__/posthog-3000-keyboard-shortcut--muted.png differ diff --git a/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png b/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png index 314ba79e762b7..cb3513e97394d 100644 Binary files a/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png and b/frontend/__snapshots__/posthog-3000-navigation--navigation-3000.png differ diff --git a/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png b/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png index c6353784ceaf1..d42c385e3c023 100644 Binary files a/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png and b/frontend/__snapshots__/posthog-3000-navigation--navigation-base.png differ diff --git a/frontend/__snapshots__/scenes-app-batchexports--create-export.png b/frontend/__snapshots__/scenes-app-batchexports--create-export.png index 5812443d7cc01..51889a6cdcc34 100644 Binary files a/frontend/__snapshots__/scenes-app-batchexports--create-export.png and b/frontend/__snapshots__/scenes-app-batchexports--create-export.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png index 189dc9741ea0f..0c9084824591a 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png index 60c850a72b2db..766de1662f8ae 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png b/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png index 0d7d6dd72c02b..9fe8b83975eb5 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png and b/frontend/__snapshots__/scenes-app-feature-flags--edit-feature-flag.png differ diff --git a/frontend/__snapshots__/scenes-app-feature-flags--edit-multi-variate-feature-flag.png b/frontend/__snapshots__/scenes-app-feature-flags--edit-multi-variate-feature-flag.png index 45cc7c6c7b4d5..4638e3c252629 100644 Binary files a/frontend/__snapshots__/scenes-app-feature-flags--edit-multi-variate-feature-flag.png and b/frontend/__snapshots__/scenes-app-feature-flags--edit-multi-variate-feature-flag.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png index 7ef2484ddde67..8be9ffc926019 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png and b/frontend/__snapshots__/scenes-app-insights--funnel-left-to-right-breakdown-edit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown.png index a0d765fdb531a..67d55e396a376 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--trends-number--webkit.png b/frontend/__snapshots__/scenes-app-insights--trends-number--webkit.png index a4feb23317dd1..2615d368706cd 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--trends-number--webkit.png and b/frontend/__snapshots__/scenes-app-insights--trends-number--webkit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--trends-number-edit--webkit.png b/frontend/__snapshots__/scenes-app-insights--trends-number-edit--webkit.png index a34e6e1d5e272..635203fb3d413 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--trends-number-edit--webkit.png and b/frontend/__snapshots__/scenes-app-insights--trends-number-edit--webkit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--trends-number-edit.png b/frontend/__snapshots__/scenes-app-insights--trends-number-edit.png index d9cf620c1a161..6924135553179 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--trends-number-edit.png and b/frontend/__snapshots__/scenes-app-insights--trends-number-edit.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--trends-number.png b/frontend/__snapshots__/scenes-app-insights--trends-number.png index 03b2456ee4782..693dd022b93e9 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--trends-number.png and b/frontend/__snapshots__/scenes-app-insights--trends-number.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png b/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png index d196998a7e56f..2b56395785ef6 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png and b/frontend/__snapshots__/scenes-app-notebooks--bullet-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png b/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png index 2086ed0aa90aa..c6df20187e100 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png and b/frontend/__snapshots__/scenes-app-notebooks--empty-notebook.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--headings.png b/frontend/__snapshots__/scenes-app-notebooks--headings.png index 3186fed28fd49..1d202bf688da1 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--headings.png and b/frontend/__snapshots__/scenes-app-notebooks--headings.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png b/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png index 0df1f64e9ec3c..6286e7ae27078 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png and b/frontend/__snapshots__/scenes-app-notebooks--notebook-not-found.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png index c9f29c566c2c6..04209a125f40b 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png and b/frontend/__snapshots__/scenes-app-notebooks--notebooks-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png b/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png index cbe5aefd199ed..de7c9e016a3d0 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png and b/frontend/__snapshots__/scenes-app-notebooks--numbered-list.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png index ae267af0034d9..b69055ad3d2ec 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--text-formats.png b/frontend/__snapshots__/scenes-app-notebooks--text-formats.png index aa872f0a5cfe9..6f7c0b4c36de0 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--text-formats.png and b/frontend/__snapshots__/scenes-app-notebooks--text-formats.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png b/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png index f0ea3f9550997..c475638688418 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png and b/frontend/__snapshots__/scenes-app-notebooks--text-only-notebook.png differ diff --git a/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png b/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png index f7ea3ec3cff02..fc3f054686e53 100644 Binary files a/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png and b/frontend/__snapshots__/scenes-app-pipeline--pipeline-app-logs.png differ diff --git a/frontend/__snapshots__/scenes-app-project-homepage--project-homepage.png b/frontend/__snapshots__/scenes-app-project-homepage--project-homepage.png index 5850c12954b74..84eb120b096bf 100644 Binary files a/frontend/__snapshots__/scenes-app-project-homepage--project-homepage.png and b/frontend/__snapshots__/scenes-app-project-homepage--project-homepage.png differ diff --git a/frontend/__snapshots__/scenes-app-saved-insights--card-view.png b/frontend/__snapshots__/scenes-app-saved-insights--card-view.png index fd1b5084ad4bd..3349db3182ce2 100644 Binary files a/frontend/__snapshots__/scenes-app-saved-insights--card-view.png and b/frontend/__snapshots__/scenes-app-saved-insights--card-view.png differ diff --git a/frontend/__snapshots__/scenes-app-saved-insights--list-view.png b/frontend/__snapshots__/scenes-app-saved-insights--list-view.png index ae911ef6d0319..8bfe21cb3985b 100644 Binary files a/frontend/__snapshots__/scenes-app-saved-insights--list-view.png and b/frontend/__snapshots__/scenes-app-saved-insights--list-view.png differ diff --git a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png index 012692ee2758d..80ccaa5e006fd 100644 Binary files a/frontend/__snapshots__/scenes-app-surveys--surveys-list.png and b/frontend/__snapshots__/scenes-app-surveys--surveys-list.png differ diff --git a/frontend/__snapshots__/scenes-other-login--second-factor.png b/frontend/__snapshots__/scenes-other-login--second-factor.png index 0eda9f6a6d221..d770df97b4345 100644 Binary files a/frontend/__snapshots__/scenes-other-login--second-factor.png and b/frontend/__snapshots__/scenes-other-login--second-factor.png differ diff --git a/frontend/__snapshots__/scenes-other-login--sso-error.png b/frontend/__snapshots__/scenes-other-login--sso-error.png index 4bac52c291407..37309681cfb75 100644 Binary files a/frontend/__snapshots__/scenes-other-login--sso-error.png and b/frontend/__snapshots__/scenes-other-login--sso-error.png differ diff --git a/frontend/__snapshots__/scenes-other-password-reset-complete--default.png b/frontend/__snapshots__/scenes-other-password-reset-complete--default.png index b08362e5ab1ab..8671219107f0c 100644 Binary files a/frontend/__snapshots__/scenes-other-password-reset-complete--default.png and b/frontend/__snapshots__/scenes-other-password-reset-complete--default.png differ diff --git a/frontend/__snapshots__/scenes-other-password-reset-complete--invalid-link.png b/frontend/__snapshots__/scenes-other-password-reset-complete--invalid-link.png index d5f6503dbfa2b..6e928b3ee74ac 100644 Binary files a/frontend/__snapshots__/scenes-other-password-reset-complete--invalid-link.png and b/frontend/__snapshots__/scenes-other-password-reset-complete--invalid-link.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-organization.png b/frontend/__snapshots__/scenes-other-settings--settings-organization.png index 240b6b4c957e5..0e5752e576d8b 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-organization.png and b/frontend/__snapshots__/scenes-other-settings--settings-organization.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-project.png b/frontend/__snapshots__/scenes-other-settings--settings-project.png index 71514ddb64f12..227e1bac41f89 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-project.png and b/frontend/__snapshots__/scenes-other-settings--settings-project.png differ diff --git a/frontend/__snapshots__/scenes-other-settings--settings-user.png b/frontend/__snapshots__/scenes-other-settings--settings-user.png index 5574601d11794..7d18a6db46dd5 100644 Binary files a/frontend/__snapshots__/scenes-other-settings--settings-user.png and b/frontend/__snapshots__/scenes-other-settings--settings-user.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar-components--flags.png b/frontend/__snapshots__/scenes-other-toolbar-components--flags.png index 43dd944036a73..10086fc86f5c1 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar-components--flags.png and b/frontend/__snapshots__/scenes-other-toolbar-components--flags.png differ diff --git a/frontend/__snapshots__/scenes-other-unsubscribe--unsubscribe-scene.png b/frontend/__snapshots__/scenes-other-unsubscribe--unsubscribe-scene.png index 4c1e72c47cc86..e0e34f6fbc61b 100644 Binary files a/frontend/__snapshots__/scenes-other-unsubscribe--unsubscribe-scene.png and b/frontend/__snapshots__/scenes-other-unsubscribe--unsubscribe-scene.png differ diff --git a/frontend/__snapshots__/scenes-other-verify-email--verify-email-invalid.png b/frontend/__snapshots__/scenes-other-verify-email--verify-email-invalid.png index ea95c8af90bc0..0291a167335d6 100644 Binary files a/frontend/__snapshots__/scenes-other-verify-email--verify-email-invalid.png and b/frontend/__snapshots__/scenes-other-verify-email--verify-email-invalid.png differ diff --git a/frontend/__snapshots__/scenes-other-verify-email--verify-email-success.png b/frontend/__snapshots__/scenes-other-verify-email--verify-email-success.png index 45b7e05851b7a..71aad6c351f8b 100644 Binary files a/frontend/__snapshots__/scenes-other-verify-email--verify-email-success.png and b/frontend/__snapshots__/scenes-other-verify-email--verify-email-success.png differ diff --git a/frontend/__snapshots__/scenes-other-verify-email--verifying-email.png b/frontend/__snapshots__/scenes-other-verify-email--verifying-email.png index a6efa516c1cda..ecfc94858c28a 100644 Binary files a/frontend/__snapshots__/scenes-other-verify-email--verifying-email.png and b/frontend/__snapshots__/scenes-other-verify-email--verifying-email.png differ diff --git a/frontend/src/exporter/ExportedInsight/ExportedInsight.tsx b/frontend/src/exporter/ExportedInsight/ExportedInsight.tsx index c75e6306f9ef8..d34818193fd4a 100644 --- a/frontend/src/exporter/ExportedInsight/ExportedInsight.tsx +++ b/frontend/src/exporter/ExportedInsight/ExportedInsight.tsx @@ -1,18 +1,20 @@ -import { ChartDisplayType, InsightLogicProps, InsightModel } from '~/types' +import './ExportedInsight.scss' + +import clsx from 'clsx' import { BindLogic } from 'kea' -import { insightLogic } from 'scenes/insights/insightLogic' import { FilterBasedCardContent } from 'lib/components/Cards/InsightCard/InsightCard' -import './ExportedInsight.scss' -import { Logo } from '~/toolbar/assets/Logo' +import { QueriesUnsupportedHere } from 'lib/components/Cards/InsightCard/QueriesUnsupportedHere' +import { TopHeading } from 'lib/components/Cards/InsightCard/TopHeading' import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend' -import { ExportOptions, ExportType } from '~/exporter/types' -import clsx from 'clsx' import { SINGLE_SERIES_DISPLAY_TYPES } from 'lib/constants' +import { insightLogic } from 'scenes/insights/insightLogic' import { isTrendsFilter } from 'scenes/insights/sharedUtils' -import { isDataTableNode } from '~/queries/utils' -import { QueriesUnsupportedHere } from 'lib/components/Cards/InsightCard/QueriesUnsupportedHere' + +import { ExportOptions, ExportType } from '~/exporter/types' import { Query } from '~/queries/Query/Query' -import { TopHeading } from 'lib/components/Cards/InsightCard/TopHeading' +import { isDataTableNode } from '~/queries/utils' +import { Logo } from '~/toolbar/assets/Logo' +import { ChartDisplayType, InsightLogicProps, InsightModel } from '~/types' export function ExportedInsight({ insight, diff --git a/frontend/src/exporter/Exporter.stories.tsx b/frontend/src/exporter/Exporter.stories.tsx index 4c5acbd7257aa..95693b3465ad0 100644 --- a/frontend/src/exporter/Exporter.stories.tsx +++ b/frontend/src/exporter/Exporter.stories.tsx @@ -1,9 +1,11 @@ -import { useEffect } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { Exporter } from './Exporter' +import { useEffect } from 'react' + import { dashboard } from '~/exporter/__mocks__/Exporter.mocks' import { ExportType } from '~/exporter/types' +import { Exporter } from './Exporter' + type Story = StoryObj const meta: Meta = { title: 'Exporter/Exporter', diff --git a/frontend/src/exporter/Exporter.tsx b/frontend/src/exporter/Exporter.tsx index 1941637123e83..0ba873c570fe1 100644 --- a/frontend/src/exporter/Exporter.tsx +++ b/frontend/src/exporter/Exporter.tsx @@ -1,18 +1,21 @@ import '~/styles' import './Exporter.scss' -import { useEffect } from 'react' -import { ExportedData, ExportType } from '~/exporter/types' -import { DashboardPlacement } from '~/types' -import { ExportedInsight } from '~/exporter/ExportedInsight/ExportedInsight' -import { Logo } from '~/toolbar/assets/Logo' -import { Dashboard } from 'scenes/dashboard/Dashboard' -import { useResizeObserver } from 'lib/hooks/useResizeObserver' -import { Link } from 'lib/lemon-ui/Link' + import clsx from 'clsx' import { useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' +import { useResizeObserver } from 'lib/hooks/useResizeObserver' +import { Link } from 'lib/lemon-ui/Link' +import { useEffect } from 'react' +import { Dashboard } from 'scenes/dashboard/Dashboard' import { SessionRecordingPlayer } from 'scenes/session-recordings/player/SessionRecordingPlayer' import { SessionRecordingPlayerMode } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { ExportedInsight } from '~/exporter/ExportedInsight/ExportedInsight' +import { ExportedData, ExportType } from '~/exporter/types' +import { Logo } from '~/toolbar/assets/Logo' +import { DashboardPlacement } from '~/types' + import { exporterViewLogic } from './exporterViewLogic' export function Exporter(props: ExportedData): JSX.Element { diff --git a/frontend/src/exporter/__mocks__/Exporter.mocks.tsx b/frontend/src/exporter/__mocks__/Exporter.mocks.tsx index fdf477f996322..6d2b7e87d1fad 100644 --- a/frontend/src/exporter/__mocks__/Exporter.mocks.tsx +++ b/frontend/src/exporter/__mocks__/Exporter.mocks.tsx @@ -1,3 +1,5 @@ +import { FunnelLayout, ShownAsValue } from 'lib/constants' + import { ChartDisplayType, DashboardTile, @@ -7,7 +9,6 @@ import { InsightShortId, InsightType, } from '~/types' -import { FunnelLayout, ShownAsValue } from 'lib/constants' export const dashboard: DashboardType = { id: 1, diff --git a/frontend/src/exporter/exporterViewLogic.ts b/frontend/src/exporter/exporterViewLogic.ts index e0c36900fc728..dcb6adf2e137c 100644 --- a/frontend/src/exporter/exporterViewLogic.ts +++ b/frontend/src/exporter/exporterViewLogic.ts @@ -1,7 +1,7 @@ import { kea, path, props, selectors } from 'kea' -import { ExportedData } from './types' import type { exporterViewLogicType } from './exporterViewLogicType' +import { ExportedData } from './types' // This is a simple logic that is mounted by the Exporter view and then can be found by any nested callers // This simplifies passing props everywhere. diff --git a/frontend/src/exporter/index.tsx b/frontend/src/exporter/index.tsx index df1660537cb44..995334d949021 100644 --- a/frontend/src/exporter/index.tsx +++ b/frontend/src/exporter/index.tsx @@ -1,10 +1,13 @@ import '~/styles' import './Exporter.scss' + import { createRoot } from 'react-dom/client' -import { loadPostHogJS } from '~/loadPostHogJS' -import { initKea } from '~/initKea' + import { Exporter } from '~/exporter/Exporter' import { ExportedData } from '~/exporter/types' +import { initKea } from '~/initKea' +import { loadPostHogJS } from '~/loadPostHogJS' + import { ErrorBoundary } from '../layout/ErrorBoundary' // Disable tracking for all exports and embeds. diff --git a/frontend/src/globals.d.ts b/frontend/src/globals.d.ts index 17bc65680725a..b03852c10a890 100644 --- a/frontend/src/globals.d.ts +++ b/frontend/src/globals.d.ts @@ -1,4 +1,5 @@ import posthog from 'posthog-js' + import { ExportedData } from '~/exporter/types' declare global { diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 42746c188f0ea..272a61f785e3d 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -1,16 +1,14 @@ import '~/styles' -import { createRoot } from 'react-dom/client' import { getContext } from 'kea' - +import posthog from 'posthog-js' +import { PostHogProvider } from 'posthog-js/react' +import { createRoot } from 'react-dom/client' import { App } from 'scenes/App' -import { initKea } from './initKea' -import { loadPostHogJS } from './loadPostHogJS' +import { initKea } from './initKea' import { ErrorBoundary } from './layout/ErrorBoundary' - -import { PostHogProvider } from 'posthog-js/react' -import posthog from 'posthog-js' +import { loadPostHogJS } from './loadPostHogJS' loadPostHogJS() initKea() diff --git a/frontend/src/initKea.ts b/frontend/src/initKea.ts index 79d727740a4dc..bb5b0af3e5525 100644 --- a/frontend/src/initKea.ts +++ b/frontend/src/initKea.ts @@ -1,13 +1,13 @@ import { KeaPlugin, resetContext } from 'kea' +import { formsPlugin } from 'kea-forms' +import { loadersPlugin } from 'kea-loaders' import { localStoragePlugin } from 'kea-localstorage' import { routerPlugin } from 'kea-router' -import { loadersPlugin } from 'kea-loaders' -import { windowValuesPlugin } from 'kea-window-values' -import { identifierToHuman } from 'lib/utils' +import { subscriptionsPlugin } from 'kea-subscriptions' import { waitForPlugin } from 'kea-waitfor' +import { windowValuesPlugin } from 'kea-window-values' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { subscriptionsPlugin } from 'kea-subscriptions' -import { formsPlugin } from 'kea-forms' +import { identifierToHuman } from 'lib/utils' /* Actions for which we don't want to show error alerts, diff --git a/frontend/src/layout/ErrorBoundary/ErrorBoundary.tsx b/frontend/src/layout/ErrorBoundary/ErrorBoundary.tsx index 456b89a8ab65b..78dde9a6d6b3a 100644 --- a/frontend/src/layout/ErrorBoundary/ErrorBoundary.tsx +++ b/frontend/src/layout/ErrorBoundary/ErrorBoundary.tsx @@ -1,8 +1,9 @@ -import { getCurrentHub, ErrorBoundary as SentryErrorBoundary } from '@sentry/react' +import './ErrorBoundary.scss' + +import { ErrorBoundary as SentryErrorBoundary, getCurrentHub } from '@sentry/react' import { HelpButton } from 'lib/components/HelpButton/HelpButton' import { IconArrowDropDown } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import './ErrorBoundary.scss' export function ErrorBoundary({ children }: { children: React.ReactElement }): JSX.Element { const isSentryInitialized = !!getCurrentHub().getClient() diff --git a/frontend/src/layout/ErrorNetwork.tsx b/frontend/src/layout/ErrorNetwork.tsx index 01473440a0801..a415d1fc8028d 100644 --- a/frontend/src/layout/ErrorNetwork.tsx +++ b/frontend/src/layout/ErrorNetwork.tsx @@ -1,5 +1,5 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconRefresh } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' export function ErrorNetwork(): JSX.Element { return ( diff --git a/frontend/src/layout/ErrorProjectUnavailable.tsx b/frontend/src/layout/ErrorProjectUnavailable.tsx index a3660e42c37cc..29a178c490b05 100644 --- a/frontend/src/layout/ErrorProjectUnavailable.tsx +++ b/frontend/src/layout/ErrorProjectUnavailable.tsx @@ -1,5 +1,6 @@ -import { PageHeader } from 'lib/components/PageHeader' import { useValues } from 'kea' +import { PageHeader } from 'lib/components/PageHeader' + import { organizationLogic } from '../scenes/organizationLogic' export function ErrorProjectUnavailable(): JSX.Element { diff --git a/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.stories.tsx b/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.stories.tsx index 45025c5d8132c..8853e4812c90a 100644 --- a/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.stories.tsx +++ b/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.stories.tsx @@ -1,9 +1,11 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { FeaturePreviewsModal as FeaturePreviewsModalComponent } from './FeaturePreviewsModal' -import { setFeatureFlags, useStorybookMocks } from '~/mocks/browser' +import { FeatureFlagKey } from 'lib/constants' import { EarlyAccessFeature } from 'posthog-js' + +import { setFeatureFlags, useStorybookMocks } from '~/mocks/browser' + import { CONSTRAINED_PREVIEWS } from './featurePreviewsLogic' -import { FeatureFlagKey } from 'lib/constants' +import { FeaturePreviewsModal as FeaturePreviewsModalComponent } from './FeaturePreviewsModal' interface StoryProps { earlyAccessFeatures: EarlyAccessFeature[] diff --git a/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.tsx b/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.tsx index 2d64c4c5d32e0..5899f59b82d47 100644 --- a/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.tsx +++ b/frontend/src/layout/FeaturePreviews/FeaturePreviewsModal.tsx @@ -1,9 +1,10 @@ import { LemonButton, LemonDivider, LemonModal, LemonSwitch, LemonTextArea, Link } from '@posthog/lemon-ui' -import { useActions, useValues, useAsyncActions } from 'kea' +import clsx from 'clsx' +import { useActions, useAsyncActions, useValues } from 'kea' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner' import { useLayoutEffect, useState } from 'react' + import { EnrichedEarlyAccessFeature, featurePreviewsLogic } from './featurePreviewsLogic' -import clsx from 'clsx' export function FeaturePreviewsModal({ inline, diff --git a/frontend/src/layout/FeaturePreviews/featurePreviewsLogic.tsx b/frontend/src/layout/FeaturePreviews/featurePreviewsLogic.tsx index 40c2e201be346..1dc54a618c214 100644 --- a/frontend/src/layout/FeaturePreviews/featurePreviewsLogic.tsx +++ b/frontend/src/layout/FeaturePreviews/featurePreviewsLogic.tsx @@ -1,12 +1,13 @@ -import { actions, kea, reducers, path, selectors, connect, listeners } from 'kea' -import { EarlyAccessFeature, posthog } from 'posthog-js' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { actionToUrl, router, urlToAction } from 'kea-router' import { supportLogic } from 'lib/components/Support/supportLogic' -import { userLogic } from 'scenes/userLogic' import { FEATURE_FLAGS, FeatureFlagKey } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { EarlyAccessFeature, posthog } from 'posthog-js' +import { userLogic } from 'scenes/userLogic' + import type { featurePreviewsLogicType } from './featurePreviewsLogicType' -import { actionToUrl, router, urlToAction } from 'kea-router' /** Features that can only be toggled if you fall under the `${flagKey}-preview` flag */ export const CONSTRAINED_PREVIEWS: Set = new Set([FEATURE_FLAGS.POSTHOG_3000]) diff --git a/frontend/src/layout/FeaturePreviews/index.ts b/frontend/src/layout/FeaturePreviews/index.ts index 08a91428b164b..6101040e9e9f2 100644 --- a/frontend/src/layout/FeaturePreviews/index.ts +++ b/frontend/src/layout/FeaturePreviews/index.ts @@ -1,2 +1,2 @@ -export { FeaturePreviewsModal } from './FeaturePreviewsModal' export { featurePreviewsLogic } from './featurePreviewsLogic' +export { FeaturePreviewsModal } from './FeaturePreviewsModal' diff --git a/frontend/src/layout/GlobalModals.tsx b/frontend/src/layout/GlobalModals.tsx index b2ae0efd30f0f..e2041d0f72dc8 100644 --- a/frontend/src/layout/GlobalModals.tsx +++ b/frontend/src/layout/GlobalModals.tsx @@ -1,19 +1,19 @@ -import { kea, path, actions, reducers, useActions, useValues } from 'kea' -import { CreateOrganizationModal } from 'scenes/organization/CreateOrganizationModal' -import { CreateProjectModal } from 'scenes/project/CreateProjectModal' - -import type { globalModalsLogicType } from './GlobalModalsType' -import { FeaturePreviewsModal } from './FeaturePreviews' -import { UpgradeModal } from 'scenes/UpgradeModal' import { LemonModal } from '@posthog/lemon-ui' -import { Setup2FA } from 'scenes/authentication/Setup2FA' -import { userLogic } from 'scenes/userLogic' -import { membersLogic } from 'scenes/organization/membersLogic' +import { actions, kea, path, reducers, useActions, useValues } from 'kea' import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { HedgehogBuddyWithLogic } from 'lib/components/HedgehogBuddy/HedgehogBuddy' import { Prompt } from 'lib/logic/newPrompt/Prompt' +import { Setup2FA } from 'scenes/authentication/Setup2FA' +import { CreateOrganizationModal } from 'scenes/organization/CreateOrganizationModal' +import { membersLogic } from 'scenes/organization/membersLogic' +import { CreateProjectModal } from 'scenes/project/CreateProjectModal' import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { InviteModal } from 'scenes/settings/organization/InviteModal' -import { HedgehogBuddyWithLogic } from 'lib/components/HedgehogBuddy/HedgehogBuddy' +import { UpgradeModal } from 'scenes/UpgradeModal' +import { userLogic } from 'scenes/userLogic' + +import { FeaturePreviewsModal } from './FeaturePreviews' +import type { globalModalsLogicType } from './GlobalModalsType' export const globalModalsLogic = kea([ path(['layout', 'navigation', 'globalModalsLogic']), diff --git a/frontend/src/layout/navigation-3000/Navigation.scss b/frontend/src/layout/navigation-3000/Navigation.scss index e727859e8c19f..74236f4e9a7fc 100644 --- a/frontend/src/layout/navigation-3000/Navigation.scss +++ b/frontend/src/layout/navigation-3000/Navigation.scss @@ -15,6 +15,12 @@ min-width: 0; overflow: auto; } + + .BridgePage { + background: none; + height: 100%; + overflow: visible; + } } .Navigation3000__scene { @@ -51,14 +57,9 @@ z-index: var(--z-main-nav); .LemonButton { - min-height: 2.25rem !important; // Reduce minimum height - - > span { + .LemonButton__chrome { padding: 0.25rem !important; - } - - .LemonButton__content { - font-size: 0.813rem; + font-size: 0.8125rem; } } @@ -71,7 +72,7 @@ } li + li { - margin-top: 0.25rem; + margin-top: -1px; } } } diff --git a/frontend/src/layout/navigation-3000/Navigation.stories.tsx b/frontend/src/layout/navigation-3000/Navigation.stories.tsx index 73d7298878007..bc31a2d8079d3 100644 --- a/frontend/src/layout/navigation-3000/Navigation.stories.tsx +++ b/frontend/src/layout/navigation-3000/Navigation.stories.tsx @@ -1,11 +1,12 @@ import { Meta } from '@storybook/react' -import { mswDecorator, setFeatureFlags } from '~/mocks/browser' -import { useEffect } from 'react' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { FEATURE_FLAGS } from 'lib/constants' +import { useEffect } from 'react' import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + +import { mswDecorator, setFeatureFlags } from '~/mocks/browser' import { EMPTY_PAGINATED_RESPONSE } from '~/mocks/handlers' -import { FEATURE_FLAGS } from 'lib/constants' const meta: Meta = { title: 'PostHog 3000/Navigation', diff --git a/frontend/src/layout/navigation-3000/Navigation.tsx b/frontend/src/layout/navigation-3000/Navigation.tsx index 522dfc2bef618..dff2a25ec7ca4 100644 --- a/frontend/src/layout/navigation-3000/Navigation.tsx +++ b/frontend/src/layout/navigation-3000/Navigation.tsx @@ -1,17 +1,20 @@ -import { CommandPalette } from 'lib/components/CommandPalette/CommandPalette' +import './Navigation.scss' + +import clsx from 'clsx' import { useMountedLogic, useValues } from 'kea' +import { CommandPalette } from 'lib/components/CommandPalette/CommandPalette' +import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { FEATURE_FLAGS } from 'lib/constants' import { ReactNode, useEffect } from 'react' +import { SceneConfig } from 'scenes/sceneTypes' + import { Breadcrumbs } from './components/Breadcrumbs' +import { MinimalNavigation } from './components/MinimalNavigation' import { Navbar } from './components/Navbar' import { Sidebar } from './components/Sidebar' -import './Navigation.scss' -import { themeLogic } from './themeLogic' import { navigation3000Logic } from './navigationLogic' -import clsx from 'clsx' -import { SceneConfig } from 'scenes/sceneTypes' -import { FlaggedFeature } from 'lib/components/FlaggedFeature' -import { FEATURE_FLAGS } from 'lib/constants' import { SidePanel } from './sidepanel/SidePanel' +import { themeLogic } from './themeLogic' export function Navigation({ children, @@ -21,16 +24,22 @@ export function Navigation({ sceneConfig: SceneConfig | null }): JSX.Element { useMountedLogic(themeLogic) - const { activeNavbarItem } = useValues(navigation3000Logic) + const { activeNavbarItem, mode } = useValues(navigation3000Logic) useEffect(() => { // FIXME: Include debug notice in a non-obstructing way document.getElementById('bottom-notice')?.remove() }, []) - if (sceneConfig?.layout === 'plain') { - return <>{children} + if (mode !== 'full') { + return ( +
+ {mode === 'minimal' ? : null} +
{children}
+
+ ) } + return (
@@ -42,6 +51,7 @@ export function Navigation({
diff --git a/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss b/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss index c143a10085e9a..51124686f5429 100644 --- a/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss +++ b/frontend/src/layout/navigation-3000/components/Breadcrumbs.scss @@ -37,7 +37,7 @@ .Breadcrumbs3000__crumbs { height: 1rem; - margin-top: 0.25rem; + margin-top: calc(0.25rem * (1 - var(--breadcrumbs-compaction-rate))); display: flex; align-items: center; overflow: visible; @@ -54,6 +54,7 @@ overflow: hidden; height: calc(1.2em * (1 - var(--breadcrumbs-compaction-rate))); box-sizing: content-box; + font-family: var(--font-sans) !important; > * { position: absolute; diff --git a/frontend/src/layout/navigation-3000/components/Breadcrumbs.tsx b/frontend/src/layout/navigation-3000/components/Breadcrumbs.tsx index cbbcc6403ae48..881c7139c6b9a 100644 --- a/frontend/src/layout/navigation-3000/components/Breadcrumbs.tsx +++ b/frontend/src/layout/navigation-3000/components/Breadcrumbs.tsx @@ -1,14 +1,16 @@ -import React, { useLayoutEffect, useState } from 'react' +import './Breadcrumbs.scss' + +import { LemonSkeleton } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' +import { EditableField } from 'lib/components/EditableField/EditableField' import { IconArrowDropDown } from 'lib/lemon-ui/icons' import { Link } from 'lib/lemon-ui/Link' -import './Breadcrumbs.scss' -import { FinalizedBreadcrumb } from '~/types' -import clsx from 'clsx' import { Popover } from 'lib/lemon-ui/Popover/Popover' +import React, { useLayoutEffect, useState } from 'react' + import { breadcrumbsLogic } from '~/layout/navigation/Breadcrumbs/breadcrumbsLogic' -import { LemonSkeleton } from '@posthog/lemon-ui' -import { EditableField } from 'lib/components/EditableField/EditableField' +import { FinalizedBreadcrumb } from '~/types' const COMPACTION_DISTANCE = 44 diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss index 6a1645dbcfd0e..1620ab43cb0ab 100644 --- a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.scss @@ -3,18 +3,27 @@ } .KeyboardShortcut__key { - display: inline-flex; align-items: center; - justify-content: center; - height: 1.25rem; - min-width: 1.25rem; - padding: 0 0.1875rem; + background: var(--accent-3000); border-radius: 0.125rem; border-width: 1px; - background: var(--accent-3000); color: var(--default); + display: inline-flex; + height: 1.25rem; + justify-content: center; + min-width: 1.25rem; + padding: 0 0.1875rem; text-transform: capitalize; + .posthog-3000 & { + border-bottom-width: 2px; + border-color: var(--secondary-3000-button-border-hover); + border-radius: 0.25rem; + font-size: 0.625rem; + padding: 0.125rem 0.25rem; + text-transform: uppercase; + } + .KeyboardShortcut--muted > & { background: none; color: var(--muted); diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx new file mode 100644 index 0000000000000..435e87ebfbdce --- /dev/null +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.stories.tsx @@ -0,0 +1,27 @@ +import { Meta } from '@storybook/react' + +import { KeyboardShortcut } from './KeyboardShortcut' + +const meta: Meta = { + title: 'PostHog 3000/Keyboard Shortcut', + component: KeyboardShortcut, + tags: ['autodocs'], +} +export default meta + +export const Default = { + args: { + cmd: true, + shift: true, + k: true, + }, +} + +export const Muted = { + args: { + muted: true, + cmd: true, + shift: true, + k: true, + }, +} diff --git a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx index a636defcd78dd..d6d5b42d7a91d 100644 --- a/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx +++ b/frontend/src/layout/navigation-3000/components/KeyboardShortcut.tsx @@ -1,7 +1,9 @@ -import { isMac } from 'lib/utils' -import { HotKeyOrModifier } from '~/types' import './KeyboardShortcut.scss' + import clsx from 'clsx' +import { isMac } from 'lib/utils' + +import { HotKeyOrModifier } from '~/types' const IS_MAC = isMac() const KEY_TO_SYMBOL: Partial> = { @@ -34,11 +36,11 @@ export function KeyboardShortcut({ muted, ...keys }: KeyboardShortcutProps): JSX ) as HotKeyOrModifier[] return ( - + {sortedKeys.map((key) => ( - - {KEY_TO_SYMBOL[key] || key} - + {KEY_TO_SYMBOL[key] || key} ))} ) diff --git a/frontend/src/layout/navigation-3000/components/MinimalNavigation.tsx b/frontend/src/layout/navigation-3000/components/MinimalNavigation.tsx new file mode 100644 index 0000000000000..1161a4dadc1c8 --- /dev/null +++ b/frontend/src/layout/navigation-3000/components/MinimalNavigation.tsx @@ -0,0 +1,62 @@ +import { IconLogomark } from '@posthog/icons' +import { LemonButton, Lettermark, Popover, ProfilePicture } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { organizationLogic } from 'scenes/organizationLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { navigationLogic } from '~/layout/navigation/navigationLogic' +import { ProjectSwitcherOverlay } from '~/layout/navigation/ProjectSwitcher' +import { SitePopoverOverlay } from '~/layout/navigation/TopBar/SitePopover' + +export function MinimalNavigation(): JSX.Element { + const { user } = useValues(userLogic) + + const { currentTeam } = useValues(teamLogic) + const { currentOrganization } = useValues(organizationLogic) + + const { isSitePopoverOpen, isProjectSwitcherShown } = useValues(navigationLogic) + const { closeSitePopover, toggleSitePopover, toggleProjectSwitcher, hideProjectSwitcher } = + useActions(navigationLogic) + + return ( + + ) +} diff --git a/frontend/src/layout/navigation-3000/components/Navbar.tsx b/frontend/src/layout/navigation-3000/components/Navbar.tsx index 3ddc730007a56..c620d0c9b927f 100644 --- a/frontend/src/layout/navigation-3000/components/Navbar.tsx +++ b/frontend/src/layout/navigation-3000/components/Navbar.tsx @@ -1,19 +1,37 @@ +import { IconAsterisk, IconDay, IconGear, IconNight, IconSearch } from '@posthog/icons' import { LemonBadge } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { IconGear, IconDay, IconNight, IconAsterisk } from '@posthog/icons' +import { commandBarLogic } from 'lib/components/CommandBar/commandBarLogic' +import { Resizer } from 'lib/components/Resizer/Resizer' import { Popover } from 'lib/lemon-ui/Popover' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { useRef } from 'react' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + import { navigationLogic } from '~/layout/navigation/navigationLogic' import { SitePopoverOverlay } from '~/layout/navigation/TopBar/SitePopover' + import { navigation3000Logic } from '../navigationLogic' import { themeLogic } from '../themeLogic' import { NavbarButton } from './NavbarButton' -import { urls } from 'scenes/urls' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { Resizer } from 'lib/components/Resizer/Resizer' -import { useRef } from 'react' + +export function ThemeIcon(): JSX.Element { + const { isDarkModeOn, isThemeSyncedWithSystem } = useValues(themeLogic) + + const activeThemeIcon = isDarkModeOn ? : + + return isThemeSyncedWithSystem ? ( +
+ {activeThemeIcon} + } /> +
+ ) : ( + activeThemeIcon + ) +} export function Navbar(): JSX.Element { const { user } = useValues(userLogic) @@ -21,12 +39,10 @@ export function Navbar(): JSX.Element { const { closeSitePopover, toggleSitePopover } = useActions(navigationLogic) const { isSidebarShown, activeNavbarItemId, navbarItems } = useValues(navigation3000Logic) const { showSidebar, hideSidebar, toggleNavCollapsed } = useActions(navigation3000Logic) - const { isDarkModeOn, darkModeSavedPreference, darkModeSystemPreference, isThemeSyncedWithSystem } = - useValues(themeLogic) + const { darkModeSavedPreference, darkModeSystemPreference } = useValues(themeLogic) const { toggleTheme } = useActions(themeLogic) const { featureFlags } = useValues(featureFlagLogic) - - const activeThemeIcon = isDarkModeOn ? : + const { toggleSearchBar } = useActions(commandBarLogic) const containerRef = useRef(null) @@ -66,16 +82,14 @@ export function Navbar(): JSX.Element {
    - {activeThemeIcon} - } /> -
- ) : ( - activeThemeIcon - ) - } + identifier="search-button" + icon={} + title="Search" + onClick={toggleSearchBar} + keyboardShortcut={{ command: true, k: true }} + /> + } identifier="theme-button" title={ darkModeSavedPreference === false @@ -96,6 +110,7 @@ export function Navbar(): JSX.Element { title="Project settings" to={urls.settings('project')} /> + } visible={isSitePopoverOpen} diff --git a/frontend/src/layout/navigation-3000/components/NavbarButton.tsx b/frontend/src/layout/navigation-3000/components/NavbarButton.tsx index 77d8ad33317b6..51996b0419c3f 100644 --- a/frontend/src/layout/navigation-3000/components/NavbarButton.tsx +++ b/frontend/src/layout/navigation-3000/components/NavbarButton.tsx @@ -1,13 +1,16 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import React, { FunctionComponent, ReactElement, useState } from 'react' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { LemonTag } from '@posthog/lemon-ui' import clsx from 'clsx' import { useValues } from 'kea' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import React, { FunctionComponent, ReactElement, useState } from 'react' import { sceneLogic } from 'scenes/sceneLogic' + import { SidebarChangeNoticeContent, useSidebarChangeNotices } from '~/layout/navigation/SideBar/SidebarChangeNotice' + import { navigation3000Logic } from '../navigationLogic' -import { LemonTag } from '@posthog/lemon-ui' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { KeyboardShortcut, KeyboardShortcutProps } from './KeyboardShortcut' export interface NavbarButtonProps { identifier: string @@ -19,94 +22,107 @@ export interface NavbarButtonProps { to?: string persistentTooltip?: boolean active?: boolean + keyboardShortcut?: KeyboardShortcutProps } export const NavbarButton: FunctionComponent = React.forwardRef< HTMLButtonElement, NavbarButtonProps ->(({ identifier, shortTitle, title, tag, onClick, persistentTooltip, ...buttonProps }, ref): JSX.Element => { - const { aliasedActiveScene } = useValues(sceneLogic) - const { isNavCollapsed } = useValues(navigation3000Logic) - const isUsingNewNav = useFeatureFlag('POSTHOG_3000_NAV') +>( + ( + { identifier, shortTitle, title, tag, onClick, persistentTooltip, keyboardShortcut, ...buttonProps }, + ref + ): JSX.Element => { + const { aliasedActiveScene } = useValues(sceneLogic) + const { isNavCollapsed } = useValues(navigation3000Logic) + const isUsingNewNav = useFeatureFlag('POSTHOG_3000_NAV') - const [hasBeenClicked, setHasBeenClicked] = useState(false) + const [hasBeenClicked, setHasBeenClicked] = useState(false) - const here = aliasedActiveScene === identifier - const isNavCollapsedActually = isNavCollapsed || isUsingNewNav + const here = aliasedActiveScene === identifier + const isNavCollapsedActually = isNavCollapsed || isUsingNewNav - if (!isUsingNewNav) { - buttonProps.active = here - } + if (!isUsingNewNav) { + buttonProps.active = here + } - let content: JSX.Element | string | undefined - if (!isNavCollapsedActually) { - content = shortTitle || title - if (tag) { - if (tag === 'alpha') { - content = ( - <> - {content} - - ALPHA - - - ) - } else if (tag === 'beta') { - content = ( - <> - {content} - - BETA - - - ) + let content: JSX.Element | string | undefined + if (!isNavCollapsedActually) { + content = shortTitle || title + if (tag) { + if (tag === 'alpha') { + content = ( + <> + {content} + + ALPHA + + + ) + } else if (tag === 'beta') { + content = ( + <> + {content} + + BETA + + + ) + } } } - } - const buttonContent = ( - setHasBeenClicked(false)} - onClick={() => { - setHasBeenClicked(true) - onClick?.() - }} - className={clsx('NavbarButton', isUsingNewNav && here && 'NavbarButton--here')} - fullWidth - type="secondary" - stealth={true} - {...buttonProps} - > - {content} - - ) + const buttonContent = ( + setHasBeenClicked(false)} + onClick={() => { + setHasBeenClicked(true) + onClick?.() + }} + className={clsx('NavbarButton', isUsingNewNav && here && 'NavbarButton--here')} + fullWidth + type="secondary" + stealth={true} + sideIcon={ + !isNavCollapsedActually && keyboardShortcut ? ( + + + + ) : null + } + {...buttonProps} + > + {content} + + ) - const [notices, onAcknowledged] = useSidebarChangeNotices({ identifier }) + const [notices, onAcknowledged] = useSidebarChangeNotices({ identifier }) - return ( -
  • - {notices.length ? ( - } - placement={notices[0].placement ?? 'right'} - delayMs={0} - visible={true} - > - {buttonContent} - - ) : ( - - {buttonContent} - - )} -
  • - ) -}) + return ( +
  • + {notices.length ? ( + } + placement={notices[0].placement ?? 'right'} + delayMs={0} + visible={true} + > + {buttonContent} + + ) : ( + + {buttonContent} + + )} +
  • + ) + } +) NavbarButton.displayName = 'NavbarButton' diff --git a/frontend/src/layout/navigation-3000/components/NewItemButton.tsx b/frontend/src/layout/navigation-3000/components/NewItemButton.tsx index bad5abf273ce8..57bffcc72b15f 100644 --- a/frontend/src/layout/navigation-3000/components/NewItemButton.tsx +++ b/frontend/src/layout/navigation-3000/components/NewItemButton.tsx @@ -1,8 +1,9 @@ -import { useActions, useValues } from 'kea' -import { SidebarCategory } from '../types' -import { navigation3000Logic } from '../navigationLogic' import { LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { IconPlus } from 'lib/lemon-ui/icons' + +import { navigation3000Logic } from '../navigationLogic' +import { SidebarCategory } from '../types' import { singularizeCategory } from './SidebarAccordion' export function NewItemButton({ category }: { category: SidebarCategory }): JSX.Element | null { diff --git a/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx b/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx index be58ae746db17..3b58b716c8e73 100644 --- a/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx +++ b/frontend/src/layout/navigation-3000/components/Sidebar.stories.tsx @@ -1,14 +1,16 @@ import { Meta } from '@storybook/react' import { useActions, useValues } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' import { useEffect } from 'react' import { Scene } from 'scenes/sceneTypes' + import { useStorybookMocks } from '~/mocks/browser' -import { navigation3000Logic } from '../navigationLogic' -import { Sidebar } from './Sidebar' -import featureFlagsJson from '../../../scenes/feature-flags/__mocks__/feature_flags.json' + import dashboardsJson from '../../../scenes/dashboard/__mocks__/dashboards.json' +import featureFlagsJson from '../../../scenes/feature-flags/__mocks__/feature_flags.json' +import { navigation3000Logic } from '../navigationLogic' import { SidebarNavbarItem } from '../types' -import { FEATURE_FLAGS } from 'lib/constants' +import { Sidebar } from './Sidebar' const meta: Meta = { title: 'PostHog 3000/Sidebar', diff --git a/frontend/src/layout/navigation-3000/components/Sidebar.tsx b/frontend/src/layout/navigation-3000/components/Sidebar.tsx index e48b2a8a45e08..c8bff277ca08f 100644 --- a/frontend/src/layout/navigation-3000/components/Sidebar.tsx +++ b/frontend/src/layout/navigation-3000/components/Sidebar.tsx @@ -2,16 +2,17 @@ import { LemonButton, LemonInput } from '@posthog/lemon-ui' import clsx from 'clsx' import { LogicWrapper, useActions, useValues } from 'kea' import { IconClose, IconMagnifier } from 'lib/lemon-ui/icons' +import { Spinner } from 'lib/lemon-ui/Spinner' +import { capitalizeFirstLetter } from 'lib/utils' import React, { useRef, useState } from 'react' +import { useDebouncedCallback } from 'use-debounce' + import { navigation3000Logic } from '../navigationLogic' -import { KeyboardShortcut } from './KeyboardShortcut' -import { SidebarAccordion, pluralizeCategory } from './SidebarAccordion' import { SidebarLogic, SidebarNavbarItem } from '../types' -import { Spinner } from 'lib/lemon-ui/Spinner' -import { useDebouncedCallback } from 'use-debounce' -import { SidebarList } from './SidebarList' +import { KeyboardShortcut } from './KeyboardShortcut' import { NewItemButton } from './NewItemButton' -import { capitalizeFirstLetter } from 'lib/utils' +import { pluralizeCategory, SidebarAccordion } from './SidebarAccordion' +import { SidebarList } from './SidebarList' /** A small delay that prevents us from making a search request on each key press. */ const SEARCH_DEBOUNCE_MS = 300 diff --git a/frontend/src/layout/navigation-3000/components/SidebarAccordion.tsx b/frontend/src/layout/navigation-3000/components/SidebarAccordion.tsx index db9d57ce5f305..03dde39b1921b 100644 --- a/frontend/src/layout/navigation-3000/components/SidebarAccordion.tsx +++ b/frontend/src/layout/navigation-3000/components/SidebarAccordion.tsx @@ -1,11 +1,12 @@ +import { useActions, useValues } from 'kea' import { IconChevronRight } from 'lib/lemon-ui/icons' -import { SidebarCategory } from '../types' import { Spinner } from 'lib/lemon-ui/Spinner' -import { SidebarList } from './SidebarList' +import { capitalizeFirstLetter } from 'lib/utils' + import { navigation3000Logic } from '../navigationLogic' -import { useActions, useValues } from 'kea' +import { SidebarCategory } from '../types' import { NewItemButton } from './NewItemButton' -import { capitalizeFirstLetter } from 'lib/utils' +import { SidebarList } from './SidebarList' interface SidebarAccordionProps { category: SidebarCategory diff --git a/frontend/src/layout/navigation-3000/components/SidebarList.tsx b/frontend/src/layout/navigation-3000/components/SidebarList.tsx index c9c76efa0aa64..7380b85422915 100644 --- a/frontend/src/layout/navigation-3000/components/SidebarList.tsx +++ b/frontend/src/layout/navigation-3000/components/SidebarList.tsx @@ -1,19 +1,20 @@ import { Link, TZLabel } from '@posthog/apps-common' +import { LemonButton, LemonTag, lemonToast } from '@posthog/lemon-ui' +import { captureException } from '@sentry/react' import clsx from 'clsx' +import { useActions, useAsyncActions, useValues } from 'kea' import { isDayjs } from 'lib/dayjs' import { IconCheckmark, IconClose, IconEllipsis } from 'lib/lemon-ui/icons' -import { BasicListItem, ExtendedListItem, ExtraListItemContext, SidebarCategory, TentativeListItem } from '../types' -import React, { useEffect, useMemo, useRef, useState } from 'react' import { LemonMenu } from 'lib/lemon-ui/LemonMenu' -import { LemonButton, LemonTag, lemonToast } from '@posthog/lemon-ui' -import { ITEM_KEY_PART_SEPARATOR, navigation3000Logic } from '../navigationLogic' -import { captureException } from '@sentry/react' -import { KeyboardShortcut } from './KeyboardShortcut' -import { List, ListProps } from 'react-virtualized/dist/es/List' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import React, { useEffect, useMemo, useRef, useState } from 'react' import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' import { InfiniteLoader } from 'react-virtualized/dist/es/InfiniteLoader' -import { useActions, useAsyncActions, useValues } from 'kea' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { List, ListProps } from 'react-virtualized/dist/es/List' + +import { ITEM_KEY_PART_SEPARATOR, navigation3000Logic } from '../navigationLogic' +import { BasicListItem, ExtendedListItem, ExtraListItemContext, SidebarCategory, TentativeListItem } from '../types' +import { KeyboardShortcut } from './KeyboardShortcut' export function SidebarList({ category }: { category: SidebarCategory }): JSX.Element { const { normalizedActiveListItemKey, sidebarWidth, newItemInlineCategory, savingNewItem } = diff --git a/frontend/src/layout/navigation-3000/navigationLogic.tsx b/frontend/src/layout/navigation-3000/navigationLogic.tsx index 5e5c1237c1b8f..31b669ee9a8c6 100644 --- a/frontend/src/layout/navigation-3000/navigationLogic.tsx +++ b/frontend/src/layout/navigation-3000/navigationLogic.tsx @@ -1,35 +1,35 @@ -import { actions, events, kea, listeners, path, props, reducers, selectors } from 'kea' -import { subscriptions } from 'kea-subscriptions' -import { BasicListItem, ExtendedListItem, NavbarItem, SidebarNavbarItem } from './types' - -import type { navigation3000LogicType } from './navigationLogicType' -import { Scene } from 'scenes/sceneTypes' -import React from 'react' -import { captureException } from '@sentry/react' -import { lemonToast } from '@posthog/lemon-ui' -import { router } from 'kea-router' -import { sceneLogic } from 'scenes/sceneLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' import { IconApps, + IconChat, IconDashboard, IconDatabase, IconGraph, IconHome, IconLive, + IconNotebook, IconPeople, IconPieChart, IconRewindPlay, + IconRocket, + IconServer, IconTestTube, IconToggle, IconToolbar, - IconNotebook, - IconRocket, - IconServer, - IconChat, } from '@posthog/icons' +import { lemonToast } from '@posthog/lemon-ui' +import { captureException } from '@sentry/react' +import { actions, connect, events, kea, listeners, path, props, reducers, selectors } from 'kea' +import { router } from 'kea-router' +import { subscriptions } from 'kea-subscriptions' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { isNotNil } from 'lib/utils' +import React from 'react' +import { sceneLogic } from 'scenes/sceneLogic' +import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + +import type { navigation3000LogicType } from './navigationLogicType' import { dashboardsSidebarLogic } from './sidebars/dashboards' import { dataManagementSidebarLogic } from './sidebars/dataManagement' import { experimentsSidebarLogic } from './sidebars/experiments' @@ -37,11 +37,13 @@ import { featureFlagsSidebarLogic } from './sidebars/featureFlags' import { insightsSidebarLogic } from './sidebars/insights' import { personsAndGroupsSidebarLogic } from './sidebars/personsAndGroups' import { toolbarSidebarLogic } from './sidebars/toolbar' -import { isNotNil } from 'lib/utils' +import { BasicListItem, ExtendedListItem, NavbarItem, SidebarNavbarItem } from './types' /** Multi-segment item keys are joined using this separator for easy comparisons. */ export const ITEM_KEY_PART_SEPARATOR = '::' +export type Navigation3000Mode = 'none' | 'minimal' | 'full' + const MINIMUM_SIDEBAR_WIDTH_PX: number = 192 const DEFAULT_SIDEBAR_WIDTH_PX: number = 288 const MAXIMUM_SIDEBAR_WIDTH_PX: number = 1024 @@ -50,6 +52,9 @@ const MAXIMUM_SIDEBAR_WIDTH_PERCENTAGE: number = 50 export const navigation3000Logic = kea([ path(['layout', 'navigation-3000', 'navigationLogic']), props({} as { inputElement?: HTMLInputElement | null }), + connect(() => ({ + values: [sceneLogic, ['sceneConfig']], + })), actions({ hideSidebar: true, showSidebar: (newNavbarItemId?: string) => ({ newNavbarItemId }), @@ -278,6 +283,16 @@ export const navigation3000Logic = kea([ }, })), selectors({ + mode: [ + (s) => [s.sceneConfig], + (sceneConfig): Navigation3000Mode => { + return sceneConfig?.layout === 'plain' && !sceneConfig.allowUnauthenticated + ? 'minimal' + : sceneConfig?.layout !== 'plain' + ? 'full' + : 'none' + }, + ], navbarItems: [ () => [featureFlagLogic.selectors.featureFlags], (featureFlags): NavbarItem[][] => { diff --git a/frontend/src/layout/navigation-3000/sidebars/annotations.tsx b/frontend/src/layout/navigation-3000/sidebars/annotations.tsx index 1db0c56ecc48d..f563a924f4f05 100644 --- a/frontend/src/layout/navigation-3000/sidebars/annotations.tsx +++ b/frontend/src/layout/navigation-3000/sidebars/annotations.tsx @@ -1,16 +1,18 @@ +import { urls } from '@posthog/apps-common' +import Fuse from 'fuse.js' import { connect, kea, path, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { AnnotationModal } from 'scenes/annotations/AnnotationModal' +import { annotationModalLogic } from 'scenes/annotations/annotationModalLogic' import { sceneLogic } from 'scenes/sceneLogic' + +import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' import { annotationsModel } from '~/models/annotationsModel' -import { SidebarCategory, ExtendedListItem } from '../types' +import { AnnotationType } from '~/types' + +import { ExtendedListItem, SidebarCategory } from '../types' import type { annotationsSidebarLogicType } from './annotationsType' -import Fuse from 'fuse.js' -import { subscriptions } from 'kea-subscriptions' -import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' import { FuseSearchMatch } from './utils' -import { AnnotationType } from '~/types' -import { urls } from '@posthog/apps-common' -import { AnnotationModal } from 'scenes/annotations/AnnotationModal' -import { annotationModalLogic } from 'scenes/annotations/annotationModalLogic' const fuse = new Fuse([], { keys: [{ name: 'content', weight: 2 }, 'date_marker'], diff --git a/frontend/src/layout/navigation-3000/sidebars/cohorts.ts b/frontend/src/layout/navigation-3000/sidebars/cohorts.ts index 5206087f6e37f..cb5c16bf4f323 100644 --- a/frontend/src/layout/navigation-3000/sidebars/cohorts.ts +++ b/frontend/src/layout/navigation-3000/sidebars/cohorts.ts @@ -1,17 +1,19 @@ +import { api } from '@posthog/apps-common' +import Fuse from 'fuse.js' import { connect, kea, path, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { dayjs } from 'lib/dayjs' +import { pluralize } from 'lib/utils' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { ExtendedListItem, SidebarCategory } from '../types' -import type { cohortsSidebarLogicType } from './cohortsType' -import Fuse from 'fuse.js' -import { CohortType } from '~/types' -import { subscriptions } from 'kea-subscriptions' + import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' import { cohortsModel } from '~/models/cohortsModel' -import { pluralize } from 'lib/utils' -import { dayjs } from 'lib/dayjs' -import { api } from '@posthog/apps-common' +import { CohortType } from '~/types' + +import { ExtendedListItem, SidebarCategory } from '../types' +import type { cohortsSidebarLogicType } from './cohortsType' import { FuseSearchMatch } from './utils' const fuse = new Fuse([], { diff --git a/frontend/src/layout/navigation-3000/sidebars/dashboards.tsx b/frontend/src/layout/navigation-3000/sidebars/dashboards.tsx index 13ae4ee92a0e9..aa313b8713f6a 100644 --- a/frontend/src/layout/navigation-3000/sidebars/dashboards.tsx +++ b/frontend/src/layout/navigation-3000/sidebars/dashboards.tsx @@ -1,22 +1,24 @@ -import { connect, kea, path, selectors } from 'kea' -import { sceneLogic } from 'scenes/sceneLogic' -import { Scene } from 'scenes/sceneTypes' -import { urls } from 'scenes/urls' -import { dashboardsModel } from '~/models/dashboardsModel' -import { SidebarCategory, BasicListItem } from '../types' -import type { dashboardsSidebarLogicType } from './dashboardsType' import Fuse from 'fuse.js' -import { DashboardBasicType, DashboardType } from '~/types' +import { connect, kea, path, selectors } from 'kea' import { subscriptions } from 'kea-subscriptions' -import { DashboardMode } from '~/types' import { DashboardEventSource } from 'lib/utils/eventUsageLogic' import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic' import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic' +import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' +import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal' +import { sceneLogic } from 'scenes/sceneLogic' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' +import { dashboardsModel } from '~/models/dashboardsModel' +import { DashboardBasicType, DashboardType } from '~/types' +import { DashboardMode } from '~/types' + +import { BasicListItem, SidebarCategory } from '../types' +import type { dashboardsSidebarLogicType } from './dashboardsType' import { FuseSearchMatch } from './utils' -import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal' -import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' const fuse = new Fuse([], { keys: [{ name: 'name', weight: 2 }, 'description', 'tags'], diff --git a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts index 3c97bfb557d9a..faaa2ee4c7e2e 100644 --- a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts +++ b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts @@ -1,17 +1,19 @@ import { actions, afterMount, connect, kea, path, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { subscriptions } from 'kea-subscriptions' +import api from 'lib/api' +import { getPropertyLabel } from 'lib/taxonomy' +import { actionsFuse, actionsLogic } from 'scenes/actions/actionsLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { SidebarCategory, BasicListItem } from '../types' + import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' +import { ActionType, EventDefinition, PropertyDefinition, ReplayTabs } from '~/types' + +import { BasicListItem, SidebarCategory } from '../types' import type { dataManagementSidebarLogicType } from './dataManagementType' import { findSearchTermInItemName } from './utils' -import { loaders } from 'kea-loaders' -import { ActionType, EventDefinition, PropertyDefinition, ReplayTabs } from '~/types' -import api from 'lib/api' -import { subscriptions } from 'kea-subscriptions' -import { getPropertyLabel } from 'lib/taxonomy' -import { actionsFuse, actionsLogic } from 'scenes/actions/actionsLogic' import { FuseSearchMatch } from './utils' export const dataManagementSidebarLogic = kea([ diff --git a/frontend/src/layout/navigation-3000/sidebars/experiments.ts b/frontend/src/layout/navigation-3000/sidebars/experiments.ts index 9d2dc2ae03515..de4ca1e8f01ae 100644 --- a/frontend/src/layout/navigation-3000/sidebars/experiments.ts +++ b/frontend/src/layout/navigation-3000/sidebars/experiments.ts @@ -1,16 +1,18 @@ +import Fuse from 'fuse.js' import { connect, kea, path, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { dayjs } from 'lib/dayjs' +import { experimentsLogic, getExperimentStatus } from 'scenes/experiments/experimentsLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { SidebarCategory, ExtendedListItem } from '../types' -import Fuse from 'fuse.js' -import { subscriptions } from 'kea-subscriptions' -import { navigation3000Logic } from '../navigationLogic' -import { FuseSearchMatch } from './utils' + import { Experiment, ProgressStatus } from '~/types' + +import { navigation3000Logic } from '../navigationLogic' +import { ExtendedListItem, SidebarCategory } from '../types' import type { experimentsSidebarLogicType } from './experimentsType' -import { experimentsLogic, getExperimentStatus } from 'scenes/experiments/experimentsLogic' -import { dayjs } from 'lib/dayjs' +import { FuseSearchMatch } from './utils' const fuse = new Fuse([], { keys: [{ name: 'name', weight: 2 }, 'description'], diff --git a/frontend/src/layout/navigation-3000/sidebars/featureFlags.tsx b/frontend/src/layout/navigation-3000/sidebars/featureFlags.tsx index cd05460eb2669..16acfa8f807e5 100644 --- a/frontend/src/layout/navigation-3000/sidebars/featureFlags.tsx +++ b/frontend/src/layout/navigation-3000/sidebars/featureFlags.tsx @@ -1,22 +1,24 @@ -import { dayjs } from 'lib/dayjs' +import Fuse from 'fuse.js' import { connect, kea, path, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { dayjs } from 'lib/dayjs' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' import { groupFilters } from 'scenes/feature-flags/FeatureFlags' import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' -import { SidebarCategory, ExtendedListItem } from '../types' -import type { featureFlagsSidebarLogicType } from './featureFlagsType' -import Fuse from 'fuse.js' + +import { groupsModel } from '~/models/groupsModel' import { FeatureFlagType } from '~/types' -import { subscriptions } from 'kea-subscriptions' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { teamLogic } from 'scenes/teamLogic' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' + import { navigation3000Logic } from '../navigationLogic' +import { ExtendedListItem, SidebarCategory } from '../types' +import type { featureFlagsSidebarLogicType } from './featureFlagsType' import { FuseSearchMatch } from './utils' -import { groupsModel } from '~/models/groupsModel' const fuse = new Fuse([], { // Note: For feature flags `name` is the description field diff --git a/frontend/src/layout/navigation-3000/sidebars/insights.ts b/frontend/src/layout/navigation-3000/sidebars/insights.ts index 71021b95246b6..577a2179c2739 100644 --- a/frontend/src/layout/navigation-3000/sidebars/insights.ts +++ b/frontend/src/layout/navigation-3000/sidebars/insights.ts @@ -1,18 +1,20 @@ +import { api } from '@posthog/apps-common' import { afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { INSIGHTS_PER_PAGE, savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' -import { SidebarCategory, BasicListItem } from '../types' -import { InsightModel } from '~/types' -import { subscriptions } from 'kea-subscriptions' + import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' -import { INSIGHTS_PER_PAGE, savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' +import { insightsModel } from '~/models/insightsModel' +import { InsightModel } from '~/types' + +import { BasicListItem, SidebarCategory } from '../types' import type { insightsSidebarLogicType } from './insightsType' import { findSearchTermInItemName } from './utils' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { teamLogic } from 'scenes/teamLogic' -import { api } from '@posthog/apps-common' -import { insightsModel } from '~/models/insightsModel' export const insightsSidebarLogic = kea([ path(['layout', 'navigation-3000', 'sidebars', 'insightsSidebarLogic']), diff --git a/frontend/src/layout/navigation-3000/sidebars/personsAndGroups.ts b/frontend/src/layout/navigation-3000/sidebars/personsAndGroups.ts index 2cefac31cf5ba..724d9c704684d 100644 --- a/frontend/src/layout/navigation-3000/sidebars/personsAndGroups.ts +++ b/frontend/src/layout/navigation-3000/sidebars/personsAndGroups.ts @@ -1,19 +1,21 @@ +import { urls } from '@posthog/apps-common' import { afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { combineUrl } from 'kea-router' +import { subscriptions } from 'kea-subscriptions' +import { groupsListLogic, GroupsPaginatedResponse } from 'scenes/groups/groupsListLogic' +import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { asDisplay, asLink } from 'scenes/persons/person-utils' +import { personsLogic } from 'scenes/persons/personsLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' -import type { personsAndGroupsSidebarLogicType } from './personsAndGroupsType' -import { personsLogic } from 'scenes/persons/personsLogic' -import { subscriptions } from 'kea-subscriptions' -import { navigation3000Logic } from '../navigationLogic' -import { SidebarCategory, BasicListItem } from '../types' -import { urls } from '@posthog/apps-common' -import { findSearchTermInItemName } from './utils' + import { groupsModel } from '~/models/groupsModel' -import { GroupsPaginatedResponse, groupsListLogic } from 'scenes/groups/groupsListLogic' -import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { combineUrl } from 'kea-router' import { PersonType } from '~/types' -import { asDisplay, asLink } from 'scenes/persons/person-utils' + +import { navigation3000Logic } from '../navigationLogic' +import { BasicListItem, SidebarCategory } from '../types' +import type { personsAndGroupsSidebarLogicType } from './personsAndGroupsType' +import { findSearchTermInItemName } from './utils' export const personsAndGroupsSidebarLogic = kea([ path(['layout', 'navigation-3000', 'sidebars', 'personsAndGroupsSidebarLogic']), @@ -98,16 +100,17 @@ export const personsAndGroupsSidebarLogic = kea { - const { searchTerm } = values - const displayId = groupDisplayId(group.group_key, group.group_properties) - return { - key: group.group_key, - name: displayId, - url: urls.group(groupType.group_type_index, group.group_key), - searchMatch: findSearchTermInItemName(displayId, searchTerm), - } as BasicListItem - }), + items: + groups[groupType.group_type_index]?.results.map((group) => { + const { searchTerm } = values + const displayId = groupDisplayId(group.group_key, group.group_properties) + return { + key: group.group_key, + name: displayId, + url: urls.group(groupType.group_type_index, group.group_key), + searchMatch: findSearchTermInItemName(displayId, searchTerm), + } as BasicListItem + }) || [], loading: groupsLoading[groupType.group_type_index], // FIXME: Add remote } as SidebarCategory) diff --git a/frontend/src/layout/navigation-3000/sidebars/toolbar.ts b/frontend/src/layout/navigation-3000/sidebars/toolbar.ts index 61875332d6981..dfb417aaf6b3b 100644 --- a/frontend/src/layout/navigation-3000/sidebars/toolbar.ts +++ b/frontend/src/layout/navigation-3000/sidebars/toolbar.ts @@ -1,21 +1,22 @@ -import { connect, kea, path, selectors } from 'kea' -import { sceneLogic } from 'scenes/sceneLogic' -import { Scene } from 'scenes/sceneTypes' -import { urls } from 'scenes/urls' -import { SidebarCategory, BasicListItem } from '../types' import Fuse from 'fuse.js' +import { connect, kea, path, selectors } from 'kea' import { subscriptions } from 'kea-subscriptions' -import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' -import { FuseSearchMatch } from './utils' import { + authorizedUrlListLogic, AuthorizedUrlListType, KeyedAppUrl, - authorizedUrlListLogic, validateProposedUrl, } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { sceneLogic } from 'scenes/sceneLogic' +import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' +import { navigation3000Logic } from '~/layout/navigation-3000/navigationLogic' + +import { BasicListItem, SidebarCategory } from '../types' import type { toolbarSidebarLogicType } from './toolbarType' -import { teamLogic } from 'scenes/teamLogic' +import { FuseSearchMatch } from './utils' const fuse = new Fuse([], { keys: ['url'], diff --git a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx index 1f85b5bf928de..d3a9ecb2fd9f3 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/SidePanel.tsx @@ -1,18 +1,21 @@ -import { LemonButton } from '@posthog/lemon-ui' import './SidePanel.scss' -import { useActions, useValues } from 'kea' -import { sidePanelLogic } from './sidePanelLogic' + +import { IconGear, IconInfo, IconNotebook, IconSupport } from '@posthog/icons' +import { LemonButton } from '@posthog/lemon-ui' import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { Resizer } from 'lib/components/Resizer/Resizer' +import { resizerLogic, ResizerLogicProps } from 'lib/components/Resizer/resizerLogic' import { useRef } from 'react' -import { ResizerLogicProps, resizerLogic } from 'lib/components/Resizer/resizerLogic' -import { IconNotebook, IconInfo, IconSupport, IconGear } from '@posthog/icons' -import { SidePanelDocs } from './panels/SidePanelDocs' -import { SidePanelSupport } from './panels/SidePanelSupport' import { NotebookPanel } from 'scenes/notebooks/NotebookPanel/NotebookPanel' + +import { SidePanelTab } from '~/types' + import { SidePanelActivation, SidePanelActivationIcon } from './panels/SidePanelActivation' +import { SidePanelDocs } from './panels/SidePanelDocs' import { SidePanelSettings } from './panels/SidePanelSettings' -import { SidePanelTab } from '~/types' +import { SidePanelSupport } from './panels/SidePanelSupport' +import { sidePanelLogic } from './sidePanelLogic' import { sidePanelStateLogic } from './sidePanelStateLogic' export const SidePanelTabs: Record = { diff --git a/frontend/src/layout/navigation-3000/sidepanel/components/SidePanelPane.tsx b/frontend/src/layout/navigation-3000/sidepanel/components/SidePanelPane.tsx index db6cab4ca16b8..7bc9a9e19e3ca 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/components/SidePanelPane.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/components/SidePanelPane.tsx @@ -1,6 +1,7 @@ import { LemonButton, Tooltip } from '@posthog/lemon-ui' import { useActions } from 'kea' import { IconClose } from 'lib/lemon-ui/icons' + import { sidePanelStateLogic } from '../sidePanelStateLogic' export function SidePanelPaneHeader({ children }: { children: React.ReactNode }): JSX.Element { diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelActivation.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelActivation.tsx index f6a137cc850ee..5cae81b23ae69 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelActivation.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelActivation.tsx @@ -1,9 +1,9 @@ import { useValues } from 'kea' +import { activationLogic, ActivationTaskType } from 'lib/components/ActivationSidebar/activationLogic' import { ActivationTask } from 'lib/components/ActivationSidebar/ActivationSidebar' -import { ActivationTaskType, activationLogic } from 'lib/components/ActivationSidebar/activationLogic' import { ProfessorHog } from 'lib/components/hedgehogs' -import { LemonProgressCircle } from 'lib/lemon-ui/LemonProgressCircle' import { LemonIconProps } from 'lib/lemon-ui/icons' +import { LemonProgressCircle } from 'lib/lemon-ui/LemonProgressCircle' export const SidePanelActivation = (): JSX.Element => { const { activeTasks, completionPercent, completedTasks } = useValues(activationLogic) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx index 413219b69f33c..e080f49c67f26 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelDocs.tsx @@ -1,11 +1,12 @@ +import { IconExternal } from '@posthog/icons' +import { LemonButton, LemonSkeleton } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { POSTHOG_WEBSITE_ORIGIN, sidePanelDocsLogic } from './sidePanelDocsLogic' import { useEffect, useRef, useState } from 'react' -import clsx from 'clsx' -import { SidePanelPaneHeader } from '../components/SidePanelPane' -import { LemonButton, LemonSkeleton } from '@posthog/lemon-ui' -import { IconExternal } from '@posthog/icons' + import { themeLogic } from '../../themeLogic' +import { SidePanelPaneHeader } from '../components/SidePanelPane' +import { POSTHOG_WEBSITE_ORIGIN, sidePanelDocsLogic } from './sidePanelDocsLogic' function SidePanelDocsSkeleton(): JSX.Element { return ( diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSettings.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSettings.tsx index 42de4417d1aba..78dc0dc520dcf 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSettings.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSettings.tsx @@ -1,13 +1,14 @@ +import { IconExternal } from '@posthog/icons' +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { sidePanelSettingsLogic } from './sidePanelSettingsLogic' +import { useEffect } from 'react' import { Settings } from 'scenes/settings/Settings' -import { LemonButton } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' import { settingsLogic } from 'scenes/settings/settingsLogic' -import { useEffect } from 'react' -import { SidePanelPaneHeader } from '../components/SidePanelPane' -import { IconExternal } from '@posthog/icons' import { SettingsLogicProps } from 'scenes/settings/types' +import { urls } from 'scenes/urls' + +import { SidePanelPaneHeader } from '../components/SidePanelPane' +import { sidePanelSettingsLogic } from './sidePanelSettingsLogic' export const SidePanelSettings = (): JSX.Element => { const { settings } = useValues(sidePanelSettingsLogic) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx index 7c028e8bd497e..e36e7661311de 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx @@ -1,11 +1,13 @@ +import { LemonDivider } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { SupportForm, SupportFormButtons } from 'lib/components/Support/SupportForm' import { supportLogic } from 'lib/components/Support/supportLogic' import { useEffect } from 'react' -import { LemonDivider } from '@posthog/lemon-ui' -import { sidePanelStateLogic } from '../sidePanelStateLogic' + import { SidePanelTab } from '~/types' +import { sidePanelStateLogic } from '../sidePanelStateLogic' + export const SidePanelSupport = (): JSX.Element => { const { closeSidePanel } = useActions(sidePanelStateLogic) const { selectedTab } = useValues(sidePanelStateLogic) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts index 7ec638c7b05a2..fa81eaedc2f32 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic.ts @@ -1,9 +1,10 @@ -import { actions, kea, reducers, path, listeners, connect, selectors } from 'kea' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { router } from 'kea-router' -import type { sidePanelDocsLogicType } from './sidePanelDocsLogicType' -import { sidePanelStateLogic } from '../sidePanelStateLogic' import { SidePanelTab } from '~/types' -import { router } from 'kea-router' + +import { sidePanelStateLogic } from '../sidePanelStateLogic' +import type { sidePanelDocsLogicType } from './sidePanelDocsLogicType' export const POSTHOG_WEBSITE_ORIGIN = 'https://posthog.com' diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx index b39077ca39523..53682a2556b53 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic.tsx @@ -1,13 +1,14 @@ -import { actions, kea, reducers, path, listeners, connect } from 'kea' -import { Settings } from 'scenes/settings/Settings' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' import { LemonDialog } from '@posthog/lemon-ui' +import { actions, connect, kea, listeners, path, reducers } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { Settings } from 'scenes/settings/Settings' +import { SettingsLogicProps } from 'scenes/settings/types' -import type { sidePanelSettingsLogicType } from './sidePanelSettingsLogicType' -import { sidePanelStateLogic } from '../sidePanelStateLogic' import { SidePanelTab } from '~/types' -import { SettingsLogicProps } from 'scenes/settings/types' + +import { sidePanelStateLogic } from '../sidePanelStateLogic' +import type { sidePanelSettingsLogicType } from './sidePanelSettingsLogicType' export const sidePanelSettingsLogic = kea([ path(['scenes', 'navigation', 'sidepanel', 'sidePanelSettingsLogic']), diff --git a/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx index a8f42f536616c..a5c97b8b897d6 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/sidePanelLogic.tsx @@ -1,11 +1,12 @@ -import { kea, path, selectors, connect } from 'kea' - -import type { sidePanelLogicType } from './sidePanelLogicType' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { connect, kea, path, selectors } from 'kea' +import { activationLogic } from 'lib/components/ActivationSidebar/activationLogic' import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { activationLogic } from 'lib/components/ActivationSidebar/activationLogic' + import { SidePanelTab } from '~/types' + +import type { sidePanelLogicType } from './sidePanelLogicType' import { sidePanelStateLogic } from './sidePanelStateLogic' export const sidePanelLogic = kea([ diff --git a/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx index 2c29443facbc6..773cfaf8d6b81 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx @@ -1,4 +1,5 @@ import { actions, kea, listeners, path, reducers } from 'kea' + import { SidePanelTab } from '~/types' import type { sidePanelStateLogicType } from './sidePanelStateLogicType' diff --git a/frontend/src/layout/navigation-3000/themeLogic.ts b/frontend/src/layout/navigation-3000/themeLogic.ts index 811960d5bf15a..2b90cd27abdee 100644 --- a/frontend/src/layout/navigation-3000/themeLogic.ts +++ b/frontend/src/layout/navigation-3000/themeLogic.ts @@ -2,15 +2,15 @@ import { actions, events, kea, path, reducers, selectors } from 'kea' import { subscriptions } from 'kea-subscriptions' import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { sceneLogic } from 'scenes/sceneLogic' import type { themeLogicType } from './themeLogicType' -import { sceneLogic } from 'scenes/sceneLogic' export const themeLogic = kea([ path(['layout', 'navigation-3000', 'themeLogic']), actions({ toggleTheme: true, - overrideTheme: (darkModePreference: boolean) => ({ darkModePreference }), + overrideTheme: (darkModePreference: boolean | null) => ({ darkModePreference }), syncDarkModePreference: (darkModePreference: boolean) => ({ darkModePreference }), }), reducers({ diff --git a/frontend/src/layout/navigation/Breadcrumbs/Breadcrumbs.tsx b/frontend/src/layout/navigation/Breadcrumbs/Breadcrumbs.tsx index 902f45cc06d0d..b33d8ad0d3324 100644 --- a/frontend/src/layout/navigation/Breadcrumbs/Breadcrumbs.tsx +++ b/frontend/src/layout/navigation/Breadcrumbs/Breadcrumbs.tsx @@ -1,12 +1,15 @@ -import React, { useState } from 'react' +import './Breadcrumbs.scss' + +import clsx from 'clsx' import { useValues } from 'kea' import { IconArrowDropDown, IconChevronRight } from 'lib/lemon-ui/icons' import { Link } from 'lib/lemon-ui/Link' -import './Breadcrumbs.scss' -import { breadcrumbsLogic } from './breadcrumbsLogic' -import { Breadcrumb as IBreadcrumb } from '~/types' -import clsx from 'clsx' import { Popover } from 'lib/lemon-ui/Popover/Popover' +import React, { useState } from 'react' + +import { Breadcrumb as IBreadcrumb } from '~/types' + +import { breadcrumbsLogic } from './breadcrumbsLogic' function Breadcrumb({ breadcrumb, index }: { breadcrumb: IBreadcrumb; index: number }): JSX.Element { const [popoverShown, setPopoverShown] = useState(false) diff --git a/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.test.ts b/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.test.ts index ce74771427690..b19bef66a11fe 100644 --- a/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.test.ts +++ b/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.test.ts @@ -1,10 +1,12 @@ -import { breadcrumbsLogic } from './breadcrumbsLogic' -import { initKeaTests } from '~/test/init' -import { expectLogic } from 'kea-test-utils' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { expectLogic } from 'kea-test-utils' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { initKeaTests } from '~/test/init' + +import { breadcrumbsLogic } from './breadcrumbsLogic' const blankScene = (): any => ({ scene: { component: () => null, logic: null } }) const scenes: any = { [Scene.SavedInsights]: blankScene, [Scene.Dashboards]: blankScene } diff --git a/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.tsx b/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.tsx index d15aefbb51a12..85a4e54243d45 100644 --- a/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.tsx +++ b/frontend/src/layout/navigation/Breadcrumbs/breadcrumbsLogic.tsx @@ -1,18 +1,21 @@ +import './Breadcrumbs.scss' + import { actions, connect, kea, listeners, path, props, reducers, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { Lettermark } from 'lib/lemon-ui/Lettermark' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { identifierToHuman, objectsEqual, stripHTTP } from 'lib/utils' import { organizationLogic } from 'scenes/organizationLogic' -import { teamLogic } from 'scenes/teamLogic' -import './Breadcrumbs.scss' -import type { breadcrumbsLogicType } from './breadcrumbsLogicType' -import { sceneLogic } from 'scenes/sceneLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { identifierToHuman, objectsEqual, stripHTTP } from 'lib/utils' +import { sceneLogic } from 'scenes/sceneLogic' +import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' -import { Lettermark } from 'lib/lemon-ui/Lettermark' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { ProjectSwitcherOverlay } from '~/layout/navigation/ProjectSwitcher' + import { OrganizationSwitcherOverlay } from '~/layout/navigation/OrganizationSwitcher' +import { ProjectSwitcherOverlay } from '~/layout/navigation/ProjectSwitcher' import { Breadcrumb, FinalizedBreadcrumb } from '~/types' -import { subscriptions } from 'kea-subscriptions' + +import type { breadcrumbsLogicType } from './breadcrumbsLogicType' export const breadcrumbsLogic = kea([ path(['layout', 'navigation', 'Breadcrumbs', 'breadcrumbsLogic']), diff --git a/frontend/src/layout/navigation/Navigation.stories.tsx b/frontend/src/layout/navigation/Navigation.stories.tsx index dbb4e54b94bbc..71d2903d628a3 100644 --- a/frontend/src/layout/navigation/Navigation.stories.tsx +++ b/frontend/src/layout/navigation/Navigation.stories.tsx @@ -1,12 +1,13 @@ -import { Meta } from '@storybook/react' -import { TopBar } from './TopBar/TopBar' -import { SideBar } from './SideBar/SideBar' -import { PageHeader } from 'lib/components/PageHeader' import { LemonButton, LemonTable } from '@posthog/lemon-ui' +import { Meta } from '@storybook/react' import { useActions } from 'kea' -import { navigationLogic } from './navigationLogic' +import { PageHeader } from 'lib/components/PageHeader' import { useEffect } from 'react' +import { navigationLogic } from './navigationLogic' +import { SideBar } from './SideBar/SideBar' +import { TopBar } from './TopBar/TopBar' + const meta: Meta = { title: 'Layout/Navigation', parameters: { diff --git a/frontend/src/layout/navigation/Navigation.tsx b/frontend/src/layout/navigation/Navigation.tsx index 18ff8d9fed01f..5dbdb204a2c5f 100644 --- a/frontend/src/layout/navigation/Navigation.tsx +++ b/frontend/src/layout/navigation/Navigation.tsx @@ -1,11 +1,12 @@ import clsx from 'clsx' import { BillingAlertsV2 } from 'lib/components/BillingAlertsV2' +import { ReactNode } from 'react' import { SceneConfig } from 'scenes/sceneTypes' + import { Breadcrumbs } from './Breadcrumbs/Breadcrumbs' import { ProjectNotice } from './ProjectNotice' import { SideBar } from './SideBar/SideBar' import { TopBar } from './TopBar/TopBar' -import { ReactNode } from 'react' export function Navigation({ children, diff --git a/frontend/src/layout/navigation/OrganizationSwitcher.tsx b/frontend/src/layout/navigation/OrganizationSwitcher.tsx index ed9232ee87256..d8894e4331344 100644 --- a/frontend/src/layout/navigation/OrganizationSwitcher.tsx +++ b/frontend/src/layout/navigation/OrganizationSwitcher.tsx @@ -9,9 +9,11 @@ import { organizationLogic } from 'scenes/organizationLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { sceneLogic } from 'scenes/sceneLogic' import { userLogic } from 'scenes/userLogic' + import { AvailableFeature, OrganizationBasicType } from '~/types' -import { navigationLogic } from './navigationLogic' + import { globalModalsLogic } from '../GlobalModals' +import { navigationLogic } from './navigationLogic' export function AccessLevelIndicator({ organization }: { organization: OrganizationBasicType }): JSX.Element { return ( diff --git a/frontend/src/layout/navigation/ProjectNotice.tsx b/frontend/src/layout/navigation/ProjectNotice.tsx index 8ac8156233e49..1305607b4caaf 100644 --- a/frontend/src/layout/navigation/ProjectNotice.tsx +++ b/frontend/src/layout/navigation/ProjectNotice.tsx @@ -1,16 +1,18 @@ import { useActions, useValues } from 'kea' -import { Link } from 'lib/lemon-ui/Link' -import { navigationLogic, ProjectNoticeVariant } from './navigationLogic' -import { inviteLogic } from 'scenes/settings/organization/inviteLogic' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { IconPlus, IconSettings } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonBannerAction } from 'lib/lemon-ui/LemonBanner/LemonBanner' -import { userLogic } from 'scenes/userLogic' +import { Link } from 'lib/lemon-ui/Link' +import { verifyEmailLogic } from 'scenes/authentication/signup/verify-email/verifyEmailLogic' import { organizationLogic } from 'scenes/organizationLogic' +import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { urls } from 'scenes/urls' -import { verifyEmailLogic } from 'scenes/authentication/signup/verify-email/verifyEmailLogic' +import { userLogic } from 'scenes/userLogic' + import { ProductKey } from '~/types' +import { navigationLogic, ProjectNoticeVariant } from './navigationLogic' + interface ProjectNoticeBlueprint { message: JSX.Element | string action?: LemonBannerAction diff --git a/frontend/src/layout/navigation/ProjectSwitcher.tsx b/frontend/src/layout/navigation/ProjectSwitcher.tsx index 25f426f7a0e38..a552fe00b5b13 100644 --- a/frontend/src/layout/navigation/ProjectSwitcher.tsx +++ b/frontend/src/layout/navigation/ProjectSwitcher.tsx @@ -9,7 +9,9 @@ import { sceneLogic } from 'scenes/sceneLogic' import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + import { AvailableFeature, TeamBasicType } from '~/types' + import { globalModalsLogic } from '../GlobalModals' export function ProjectName({ team }: { team: TeamBasicType }): JSX.Element { diff --git a/frontend/src/layout/navigation/SideBar/PageButton.tsx b/frontend/src/layout/navigation/SideBar/PageButton.tsx index 1e8fafeca07de..0086001ada2e2 100644 --- a/frontend/src/layout/navigation/SideBar/PageButton.tsx +++ b/frontend/src/layout/navigation/SideBar/PageButton.tsx @@ -1,12 +1,13 @@ import { useActions, useValues } from 'kea' -import { sceneLogic } from 'scenes/sceneLogic' -import { navigationLogic } from '~/layout/navigation/navigationLogic' -import { dashboardsModel } from '~/models/dashboardsModel' -import { Scene } from 'scenes/sceneTypes' import { LemonButton, LemonButtonProps, LemonButtonWithSideAction, SideAction } from 'lib/lemon-ui/LemonButton' -import { sceneConfigurations } from 'scenes/scenes' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { sceneLogic } from 'scenes/sceneLogic' +import { sceneConfigurations } from 'scenes/scenes' +import { Scene } from 'scenes/sceneTypes' + +import { navigationLogic } from '~/layout/navigation/navigationLogic' import { SidebarChangeNoticeTooltip } from '~/layout/navigation/SideBar/SidebarChangeNotice' +import { dashboardsModel } from '~/models/dashboardsModel' export interface PageButtonProps extends Pick { /** Used for highlighting the active scene. `identifier` of type number means dashboard ID instead of scene. */ diff --git a/frontend/src/layout/navigation/SideBar/SideBar.tsx b/frontend/src/layout/navigation/SideBar/SideBar.tsx index b5f8c1e3f8ef3..c8811625e90a0 100644 --- a/frontend/src/layout/navigation/SideBar/SideBar.tsx +++ b/frontend/src/layout/navigation/SideBar/SideBar.tsx @@ -1,8 +1,12 @@ +import './SideBar.scss' + import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { Link } from 'lib/lemon-ui/Link' -import { useState } from 'react' -import { ProjectName, ProjectSwitcherOverlay } from '~/layout/navigation/ProjectSwitcher' +import { ActivationSidebar } from 'lib/components/ActivationSidebar/ActivationSidebar' +import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { DebugNotice } from 'lib/components/DebugNotice' +import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { FEATURE_FLAGS } from 'lib/constants' import { IconApps, IconBarChart, @@ -25,8 +29,22 @@ import { IconUnverifiedEvent, IconWeb, } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { Lettermark } from 'lib/lemon-ui/Lettermark' +import { Link } from 'lib/lemon-ui/Link' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { useState } from 'react' +import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' +import { IconNotebook } from 'scenes/notebooks/IconNotebook' +import { NotebookPopover } from 'scenes/notebooks/NotebookPanel/NotebookPopover' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' + +import { ProjectName, ProjectSwitcherOverlay } from '~/layout/navigation/ProjectSwitcher' +import { PageButton } from '~/layout/navigation/SideBar/PageButton' +import { SideBarApps } from '~/layout/navigation/SideBar/SideBarApps' import { dashboardsModel } from '~/models/dashboardsModel' import { organizationLogic } from '~/scenes/organizationLogic' import { canViewPlugins } from '~/scenes/plugins/access' @@ -34,23 +52,8 @@ import { Scene } from '~/scenes/sceneTypes' import { isAuthenticatedTeam, teamLogic } from '~/scenes/teamLogic' import { urls } from '~/scenes/urls' import { AvailableFeature } from '~/types' -import './SideBar.scss' + import { navigationLogic } from '../navigationLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { userLogic } from 'scenes/userLogic' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { SideBarApps } from '~/layout/navigation/SideBar/SideBarApps' -import { PageButton } from '~/layout/navigation/SideBar/PageButton' -import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' -import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { DebugNotice } from 'lib/components/DebugNotice' -import { NotebookPopover } from 'scenes/notebooks/NotebookPanel/NotebookPopover' -import { FlaggedFeature } from 'lib/components/FlaggedFeature' -import { IconNotebook } from 'scenes/notebooks/IconNotebook' -import { ActivationSidebar } from 'lib/components/ActivationSidebar/ActivationSidebar' function Pages(): JSX.Element { const { currentOrganization } = useValues(organizationLogic) diff --git a/frontend/src/layout/navigation/SideBar/SideBarApps.tsx b/frontend/src/layout/navigation/SideBar/SideBarApps.tsx index 950fff098e82c..412ab375a10af 100644 --- a/frontend/src/layout/navigation/SideBar/SideBarApps.tsx +++ b/frontend/src/layout/navigation/SideBar/SideBarApps.tsx @@ -1,16 +1,17 @@ +import { useActions, useValues } from 'kea' +import { router } from 'kea-router' import { IconExtension } from 'lib/lemon-ui/icons' -import { urls } from 'scenes/urls' -import { Scene } from 'scenes/sceneTypes' -import { canInstallPlugins } from 'scenes/plugins/access' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { PluginSource } from 'scenes/plugins/source/PluginSource' -import { useActions, useValues } from 'kea' +import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' import { organizationLogic } from 'scenes/organizationLogic' +import { canInstallPlugins } from 'scenes/plugins/access' +import { PluginSource } from 'scenes/plugins/source/PluginSource' +import { PluginInstallationType } from 'scenes/plugins/types' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { navigationLogic } from '~/layout/navigation/navigationLogic' -import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' -import { router } from 'kea-router' import { PageButton } from '~/layout/navigation/SideBar/PageButton' -import { PluginInstallationType } from 'scenes/plugins/types' export function SideBarApps(): JSX.Element { const { currentOrganization } = useValues(organizationLogic) diff --git a/frontend/src/layout/navigation/TopBar/Announcement.tsx b/frontend/src/layout/navigation/TopBar/Announcement.tsx index 24b0d3e73ec6b..04fc851df88e5 100644 --- a/frontend/src/layout/navigation/TopBar/Announcement.tsx +++ b/frontend/src/layout/navigation/TopBar/Announcement.tsx @@ -1,12 +1,13 @@ +import { LemonButton, Link } from '@posthog/lemon-ui' import clsx from 'clsx' -import { MOCK_NODE_PROCESS } from 'lib/constants' -import { announcementLogic, AnnouncementType } from '~/layout/navigation/TopBar/announcementLogic' import { useActions, useValues } from 'kea' +import { MOCK_NODE_PROCESS } from 'lib/constants' import { NewFeatureBanner } from 'lib/introductions/NewFeatureBanner' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { LemonButton, Link } from '@posthog/lemon-ui' import { IconClose } from 'lib/lemon-ui/icons' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { announcementLogic, AnnouncementType } from '~/layout/navigation/TopBar/announcementLogic' window.process = MOCK_NODE_PROCESS diff --git a/frontend/src/layout/navigation/TopBar/NotebookButton.tsx b/frontend/src/layout/navigation/TopBar/NotebookButton.tsx index de32610df9620..87e08761ffd06 100644 --- a/frontend/src/layout/navigation/TopBar/NotebookButton.tsx +++ b/frontend/src/layout/navigation/TopBar/NotebookButton.tsx @@ -1,5 +1,5 @@ -import { useActions, useValues } from 'kea' import { LemonButton, LemonButtonWithSideActionProps } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { IconNotebook } from 'scenes/notebooks/IconNotebook' import { notebookPanelLogic } from 'scenes/notebooks/NotebookPanel/notebookPanelLogic' diff --git a/frontend/src/layout/navigation/TopBar/NotificationBell.tsx b/frontend/src/layout/navigation/TopBar/NotificationBell.tsx index 997c7a2ce3803..206086bfafc47 100644 --- a/frontend/src/layout/navigation/TopBar/NotificationBell.tsx +++ b/frontend/src/layout/navigation/TopBar/NotificationBell.tsx @@ -1,16 +1,18 @@ -import { IconArrowDropDown, IconInfo, IconNotification, IconWithCount } from 'lib/lemon-ui/icons' -import { notificationsLogic } from '~/layout/navigation/TopBar/notificationsLogic' -import { useActions, useValues } from 'kea' +import './NotificationsBell.scss' + import clsx from 'clsx' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { usePageVisibility } from 'lib/hooks/usePageVisibility' +import { useActions, useValues } from 'kea' import { ActivityLogRow } from 'lib/components/ActivityLog/ActivityLog' -import './NotificationsBell.scss' +import { usePageVisibility } from 'lib/hooks/usePageVisibility' +import { IconArrowDropDown, IconInfo, IconNotification, IconWithCount } from 'lib/lemon-ui/icons' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { Link } from 'lib/lemon-ui/Link' +import { Popover } from 'lib/lemon-ui/Popover/Popover' import { urls } from 'scenes/urls' +import { notificationsLogic } from '~/layout/navigation/TopBar/notificationsLogic' + export function NotificationBell(): JSX.Element { const { unreadCount, hasNotifications, notifications, isNotificationPopoverOpen, hasUnread } = useValues(notificationsLogic) diff --git a/frontend/src/layout/navigation/TopBar/SitePopover.tsx b/frontend/src/layout/navigation/TopBar/SitePopover.tsx index 4761e5f40f5ed..4a5e4ee0e9f58 100644 --- a/frontend/src/layout/navigation/TopBar/SitePopover.tsx +++ b/frontend/src/layout/navigation/TopBar/SitePopover.tsx @@ -1,46 +1,48 @@ +import { IconLive } from '@posthog/icons' +import { LemonButtonPropsBase } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { userLogic } from '../../../scenes/userLogic' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { hedgehogbuddyLogic } from 'lib/components/HedgehogBuddy/hedgehogbuddyLogic' +import { FEATURE_FLAGS } from 'lib/constants' import { - IconCheckmark, - IconOffline, - IconLogout, - IconUpdate, - IconExclamation, - IconBill, IconArrowDropDown, - IconSettings, + IconBill, + IconCheckmark, IconCorporate, + IconExclamation, + IconFlare, + IconLogout, + IconOffline, IconPlus, IconRedeem, - IconFlare, + IconSettings, + IconUpdate, } from 'lib/lemon-ui/icons' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from '../../../scenes/urls' -import { navigationLogic } from '../navigationLogic' -import { OrganizationBasicType } from '../../../types' -import { organizationLogic } from '../../../scenes/organizationLogic' -import { preflightLogic } from '../../../scenes/PreflightCheck/preflightLogic' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonRow } from 'lib/lemon-ui/LemonRow' import { Lettermark } from 'lib/lemon-ui/Lettermark' +import { Link } from 'lib/lemon-ui/Link' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { billingLogic } from 'scenes/billing/billingLogic' +import { inviteLogic } from 'scenes/settings/organization/inviteLogic' + +import { featurePreviewsLogic } from '~/layout/FeaturePreviews/featurePreviewsLogic' import { AccessLevelIndicator, NewOrganizationButton, OtherOrganizationButton, } from '~/layout/navigation/OrganizationSwitcher' -import { inviteLogic } from 'scenes/settings/organization/inviteLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { LemonButtonPropsBase } from '@posthog/lemon-ui' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { billingLogic } from 'scenes/billing/billingLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { FlaggedFeature } from 'lib/components/FlaggedFeature' -import { featurePreviewsLogic } from '~/layout/FeaturePreviews/featurePreviewsLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { IconLive } from '@posthog/icons' -import { hedgehogbuddyLogic } from 'lib/components/HedgehogBuddy/hedgehogbuddyLogic' + +import { organizationLogic } from '../../../scenes/organizationLogic' +import { preflightLogic } from '../../../scenes/PreflightCheck/preflightLogic' +import { urls } from '../../../scenes/urls' +import { userLogic } from '../../../scenes/userLogic' +import { OrganizationBasicType } from '../../../types' +import { navigationLogic } from '../navigationLogic' function SitePopoverSection({ title, children }: { title?: string | JSX.Element; children: any }): JSX.Element { return ( diff --git a/frontend/src/layout/navigation/TopBar/TopBar.tsx b/frontend/src/layout/navigation/TopBar/TopBar.tsx index 9c307caa1c6f7..73252e825e95e 100644 --- a/frontend/src/layout/navigation/TopBar/TopBar.tsx +++ b/frontend/src/layout/navigation/TopBar/TopBar.tsx @@ -1,24 +1,27 @@ +import './TopBar.scss' + +import { LemonButtonWithDropdown, Lettermark } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { Logo } from '~/toolbar/assets/Logo' -import { SitePopover } from './SitePopover' -import { Announcement } from './Announcement' -import { navigationLogic } from '../navigationLogic' -import { HelpButton } from 'lib/components/HelpButton/HelpButton' +import { ActivationSidebarToggle } from 'lib/components/ActivationSidebar/ActivationSidebarToggle' import { CommandPalette } from 'lib/components/CommandPalette/CommandPalette' -import { Link } from 'lib/lemon-ui/Link' -import { IconMenu, IconMenuOpen } from 'lib/lemon-ui/icons' -import './TopBar.scss' -import { UniversalSearchPopover } from 'lib/components/UniversalSearch/UniversalSearchPopover' +import { HelpButton } from 'lib/components/HelpButton/HelpButton' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { groupsModel } from '~/models/groupsModel' -import { NotificationBell } from '~/layout/navigation/TopBar/NotificationBell' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { UniversalSearchPopover } from 'lib/components/UniversalSearch/UniversalSearchPopover' import { FEATURE_FLAGS } from 'lib/constants' -import { NotebookButton } from '~/layout/navigation/TopBar/NotebookButton' -import { ActivationSidebarToggle } from 'lib/components/ActivationSidebar/ActivationSidebarToggle' +import { IconMenu, IconMenuOpen } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { organizationLogic } from 'scenes/organizationLogic' -import { LemonButtonWithDropdown, Lettermark } from '@posthog/lemon-ui' + +import { NotebookButton } from '~/layout/navigation/TopBar/NotebookButton' +import { NotificationBell } from '~/layout/navigation/TopBar/NotificationBell' +import { groupsModel } from '~/models/groupsModel' +import { Logo } from '~/toolbar/assets/Logo' + +import { navigationLogic } from '../navigationLogic' import { ProjectSwitcherOverlay } from '../ProjectSwitcher' +import { Announcement } from './Announcement' +import { SitePopover } from './SitePopover' import { topBarLogic } from './topBarLogic' export function TopBar(): JSX.Element { diff --git a/frontend/src/layout/navigation/TopBar/announcementLogic.test.ts b/frontend/src/layout/navigation/TopBar/announcementLogic.test.ts index ecef00b2cdfb0..562a5ec022e59 100644 --- a/frontend/src/layout/navigation/TopBar/announcementLogic.test.ts +++ b/frontend/src/layout/navigation/TopBar/announcementLogic.test.ts @@ -1,13 +1,15 @@ +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import { announcementLogic, AnnouncementType, DEFAULT_CLOUD_ANNOUNCEMENT } from './announcementLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + +import { initKeaTests } from '~/test/init' + import { navigationLogic } from '../navigationLogic' -import { FEATURE_FLAGS } from 'lib/constants' +import { announcementLogic, AnnouncementType, DEFAULT_CLOUD_ANNOUNCEMENT } from './announcementLogic' describe('announcementLogic', () => { let logic: ReturnType diff --git a/frontend/src/layout/navigation/TopBar/announcementLogic.ts b/frontend/src/layout/navigation/TopBar/announcementLogic.ts index 60e0b5915b2f0..593ef92032830 100644 --- a/frontend/src/layout/navigation/TopBar/announcementLogic.ts +++ b/frontend/src/layout/navigation/TopBar/announcementLogic.ts @@ -1,12 +1,12 @@ -import { kea, connect, path, actions, reducers, selectors } from 'kea' +import { actions, connect, kea, path, reducers, selectors } from 'kea' import { router } from 'kea-router' import { FEATURE_FLAGS, OrganizationMembershipLevel } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import posthog from 'posthog-js' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { userLogic } from 'scenes/userLogic' -import { navigationLogic } from '../navigationLogic' -import posthog from 'posthog-js' +import { navigationLogic } from '../navigationLogic' import type { announcementLogicType } from './announcementLogicType' export enum AnnouncementType { diff --git a/frontend/src/layout/navigation/TopBar/notificationsLogic.tsx b/frontend/src/layout/navigation/TopBar/notificationsLogic.tsx index 4b25f0afdc43f..1dcf216b92566 100644 --- a/frontend/src/layout/navigation/TopBar/notificationsLogic.tsx +++ b/frontend/src/layout/navigation/TopBar/notificationsLogic.tsx @@ -1,14 +1,14 @@ import { actions, events, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' -import { teamLogic } from 'scenes/teamLogic' -import { ActivityLogItem, humanize, HumanizedActivityLogItem } from 'lib/components/ActivityLog/humanizeActivity' - -import type { notificationsLogicType } from './notificationsLogicType' import { describerFor } from 'lib/components/ActivityLog/activityLogLogic' +import { ActivityLogItem, humanize, HumanizedActivityLogItem } from 'lib/components/ActivityLog/humanizeActivity' import { dayjs } from 'lib/dayjs' -import posthog from 'posthog-js' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import posthog from 'posthog-js' +import { teamLogic } from 'scenes/teamLogic' + +import type { notificationsLogicType } from './notificationsLogicType' const POLL_TIMEOUT = 5 * 60 * 1000 const MARK_READ_TIMEOUT = 2500 diff --git a/frontend/src/layout/navigation/TopBar/topBarLogic.ts b/frontend/src/layout/navigation/TopBar/topBarLogic.ts index 3ef8f08138acd..a1c2f8ce00a70 100644 --- a/frontend/src/layout/navigation/TopBar/topBarLogic.ts +++ b/frontend/src/layout/navigation/TopBar/topBarLogic.ts @@ -1,4 +1,4 @@ -import { actions, kea, reducers, path } from 'kea' +import { actions, kea, path, reducers } from 'kea' import type { topBarLogicType } from './topBarLogicType' diff --git a/frontend/src/layout/navigation/navigationLogic.ts b/frontend/src/layout/navigation/navigationLogic.ts index f819384e973e3..2b11ef83f4a38 100644 --- a/frontend/src/layout/navigation/navigationLogic.ts +++ b/frontend/src/layout/navigation/navigationLogic.ts @@ -1,16 +1,16 @@ -import { windowValues } from 'kea-window-values' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners } from 'kea' +import { windowValues } from 'kea-window-values' import api from 'lib/api' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { membersLogic } from 'scenes/organization/membersLogic' import { organizationLogic } from 'scenes/organizationLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { sceneLogic } from 'scenes/sceneLogic' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' + import type { navigationLogicType } from './navigationLogicType' -import { membersLogic } from 'scenes/organization/membersLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { Scene } from 'scenes/sceneTypes' export type ProjectNoticeVariant = | 'demo_project' @@ -22,7 +22,7 @@ export type ProjectNoticeVariant = export const navigationLogic = kea([ path(['layout', 'navigation', 'navigationLogic']), connect(() => ({ - values: [sceneLogic, ['sceneConfig', 'activeScene'], membersLogic, ['members', 'membersLoading']], + values: [sceneLogic, ['sceneConfig'], membersLogic, ['members', 'membersLoading']], actions: [eventUsageLogic, ['reportProjectNoticeDismissed']], })), actions({ @@ -121,10 +121,9 @@ export const navigationLogic = kea([ (fullscreen, sceneConfig) => fullscreen || sceneConfig?.layout === 'plain', ], minimalTopBar: [ - (s) => [s.activeScene], - (activeScene) => { - const minimalTopBarScenes = [Scene.Products, Scene.Onboarding] - return activeScene && minimalTopBarScenes.includes(activeScene) + (s) => [s.sceneConfig], + (sceneConfig) => { + return sceneConfig?.layout === 'plain' && !sceneConfig.allowUnauthenticated }, ], isSideBarShown: [ diff --git a/frontend/src/lib/Chart.ts b/frontend/src/lib/Chart.ts index e1575707bd4a1..69e8ee04f44d3 100644 --- a/frontend/src/lib/Chart.ts +++ b/frontend/src/lib/Chart.ts @@ -7,16 +7,16 @@ import { ChartOptions, ChartType, Color, + DefaultDataPoint, + GridLineOptions, InteractionItem, + Plugin, + registerables, + ScriptableLineSegmentContext, TickOptions, - GridLineOptions, + Tooltip, TooltipModel, TooltipOptions, - ScriptableLineSegmentContext, - DefaultDataPoint, - Tooltip, - Plugin, - registerables, } from 'chart.js' import CrosshairPlugin from 'chartjs-plugin-crosshair' import { inStorybookTestRunner } from 'lib/utils' @@ -56,11 +56,11 @@ export { ChartOptions, ChartType, Color, + GridLineOptions, InteractionItem, + Plugin, + ScriptableLineSegmentContext, TickOptions, - GridLineOptions, TooltipModel, TooltipOptions, - Plugin, - ScriptableLineSegmentContext, } diff --git a/frontend/src/lib/actionUtils.test.ts b/frontend/src/lib/actionUtils.test.ts index e08c8e6df35fb..69246099b53ed 100644 --- a/frontend/src/lib/actionUtils.test.ts +++ b/frontend/src/lib/actionUtils.test.ts @@ -1,4 +1,5 @@ import { elementToSelector } from 'lib/actionUtils' + import { ElementType } from '~/types' describe('elementToSelector', () => { diff --git a/frontend/src/lib/actionUtils.ts b/frontend/src/lib/actionUtils.ts index 26f3def245c02..fb6ac933ac4d3 100644 --- a/frontend/src/lib/actionUtils.ts +++ b/frontend/src/lib/actionUtils.ts @@ -1,6 +1,7 @@ -import { ElementType } from '~/types' import { cssEscape } from 'lib/utils/cssEscape' +import { ElementType } from '~/types' + // these plus any element with cursor:pointer will be click targets export const CLICK_TARGETS = ['a', 'button', 'input', 'select', 'textarea', 'label'] export const CLICK_TARGET_SELECTOR = CLICK_TARGETS.join(', ') diff --git a/frontend/src/lib/api.mock.ts b/frontend/src/lib/api.mock.ts index beebac7314bc7..84602fe23d41c 100644 --- a/frontend/src/lib/api.mock.ts +++ b/frontend/src/lib/api.mock.ts @@ -1,3 +1,6 @@ +import apiReal from 'lib/api' +import { PluginInstallationType } from 'scenes/plugins/types' + import { CohortType, FilterLogicalOperator, @@ -14,9 +17,8 @@ import { UserBasicType, UserType, } from '~/types' + import { OrganizationMembershipLevel, PluginsAccessLevel } from './constants' -import apiReal from 'lib/api' -import { PluginInstallationType } from 'scenes/plugins/types' export const MOCK_USER_UUID: UserType['uuid'] = 'USER_UUID' export const MOCK_TEAM_ID: TeamType['id'] = 997 diff --git a/frontend/src/lib/api.test.ts b/frontend/src/lib/api.test.ts index 8657a253f95e2..9fdcb3ec5410e 100644 --- a/frontend/src/lib/api.test.ts +++ b/frontend/src/lib/api.test.ts @@ -1,7 +1,8 @@ import api from 'lib/api' -import { PropertyFilterType, PropertyOperator } from '~/types' import posthog from 'posthog-js' +import { PropertyFilterType, PropertyOperator } from '~/types' + describe('API helper', () => { let fakeFetch: jest.Mock diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index d51c4226ab69b..6e3ce2cb92e6c 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -1,18 +1,27 @@ -import posthog from 'posthog-js' import { decompressSync, strFromU8 } from 'fflate' import { encodeParams } from 'kea-router' +import { ActivityLogProps } from 'lib/components/ActivityLog/ActivityLog' +import { ActivityLogItem, ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { toParams } from 'lib/utils' +import posthog from 'posthog-js' +import { SavedSessionRecordingPlaylistsResult } from 'scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic' +import { getCurrentExporterData } from '~/exporter/exporterViewLogic' +import { QuerySchema, QueryStatus } from '~/queries/schema' import { ActionType, + BatchExportConfiguration, BatchExportLogEntry, + BatchExportRun, CohortType, DashboardCollaboratorType, DashboardTemplateEditorType, DashboardTemplateListParams, DashboardTemplateType, DashboardType, - DataWarehouseTable, DataWarehouseSavedQuery, + DataWarehouseTable, + DataWarehouseViewLink, EarlyAccessFeatureType, EventDefinition, EventDefinitionType, @@ -20,15 +29,18 @@ import { EventType, Experiment, ExportedAssetType, + ExternalDataStripeSource, + ExternalDataStripeSourceCreatePayload, FeatureFlagAssociatedRoleType, FeatureFlagType, - OrganizationFeatureFlags, - OrganizationFeatureFlagsCopyBody, InsightModel, IntegrationType, MediaUploadResponse, NewEarlyAccessFeatureType, + NotebookNodeResource, NotebookType, + OrganizationFeatureFlags, + OrganizationFeatureFlagsCopyBody, OrganizationResourcePermissionType, OrganizationType, PersonListParams, @@ -49,15 +61,10 @@ import { SubscriptionType, Survey, TeamType, - UserType, - DataWarehouseViewLink, - BatchExportConfiguration, - BatchExportRun, UserBasicType, - NotebookNodeResource, - ExternalDataStripeSourceCreatePayload, - ExternalDataStripeSource, + UserType, } from '~/types' + import { ACTIVITY_PAGE_SIZE, DashboardPrivilegeLevel, @@ -65,12 +72,6 @@ import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE, LOGS_PORTION_LIMIT, } from './constants' -import { toParams } from 'lib/utils' -import { ActivityLogItem, ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { ActivityLogProps } from 'lib/components/ActivityLog/ActivityLog' -import { SavedSessionRecordingPlaylistsResult } from 'scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic' -import { QuerySchema, QueryStatus } from '~/queries/schema' -import { getCurrentExporterData } from '~/exporter/exporterViewLogic' /** * WARNING: Be very careful importing things here. This file is heavily used and can trigger a lot of cyclic imports @@ -481,6 +482,13 @@ class ApiRequest { return this.featureFlags(teamId).addPathComponent(id) } + public featureFlagCreateStaticCohort(id: FeatureFlagType['id'], teamId?: TeamType['id']): ApiRequest { + if (!id) { + throw new Error('Must provide an ID for the feature flag to construct the URL') + } + return this.featureFlag(id, teamId).addPathComponent('create_static_cohort_for_flag') + } + public featureFlagsActivity(id: FeatureFlagType['id'], teamId: TeamType['id']): ApiRequest { if (id) { return this.featureFlag(id, teamId).addPathComponent('activity') @@ -728,6 +736,9 @@ const api = { async get(id: FeatureFlagType['id']): Promise { return await new ApiRequest().featureFlag(id).get() }, + async createStaticCohort(id: FeatureFlagType['id']): Promise<{ cohort: CohortType }> { + return await new ApiRequest().featureFlagCreateStaticCohort(id).create() + }, }, organizationFeatureFlags: { diff --git a/frontend/src/lib/components/ActivationSidebar/ActivationSidebar.tsx b/frontend/src/lib/components/ActivationSidebar/ActivationSidebar.tsx index 2e66f6e854a5a..cb4bcdc9cd6d1 100644 --- a/frontend/src/lib/components/ActivationSidebar/ActivationSidebar.tsx +++ b/frontend/src/lib/components/ActivationSidebar/ActivationSidebar.tsx @@ -1,14 +1,17 @@ +import './ActivationSidebar.scss' + import { LemonButton, LemonButtonProps, LemonButtonWithSideAction } from '@posthog/lemon-ui' +import { Progress } from 'antd' import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { navigationLogic } from '~/layout/navigation/navigationLogic' -import { activationLogic, ActivationTaskType } from './activationLogic' -import './ActivationSidebar.scss' -import { Progress } from 'antd' import { IconCheckmark, IconClose } from 'lib/lemon-ui/icons' -import { ProfessorHog } from '../hedgehogs' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { navigationLogic } from '~/layout/navigation/navigationLogic' + +import { ProfessorHog } from '../hedgehogs' +import { activationLogic, ActivationTaskType } from './activationLogic' + export const ActivationTask = ({ id, name, diff --git a/frontend/src/lib/components/ActivationSidebar/ActivationSidebarToggle.tsx b/frontend/src/lib/components/ActivationSidebar/ActivationSidebarToggle.tsx index f123a00247135..6e2675bfc6741 100644 --- a/frontend/src/lib/components/ActivationSidebar/ActivationSidebarToggle.tsx +++ b/frontend/src/lib/components/ActivationSidebar/ActivationSidebarToggle.tsx @@ -1,7 +1,9 @@ import { LemonButton } from '@posthog/lemon-ui' +import { Progress } from 'antd' import { useActions, useValues } from 'kea' + import { navigationLogic } from '~/layout/navigation/navigationLogic' -import { Progress } from 'antd' + import { activationLogic } from './activationLogic' export const ActivationSidebarToggle = (): JSX.Element | null => { diff --git a/frontend/src/lib/components/ActivationSidebar/activationLogic.test.ts b/frontend/src/lib/components/ActivationSidebar/activationLogic.test.ts index 064e9d442b607..c9c61a55e8be6 100644 --- a/frontend/src/lib/components/ActivationSidebar/activationLogic.test.ts +++ b/frontend/src/lib/components/ActivationSidebar/activationLogic.test.ts @@ -1,10 +1,12 @@ import { expectLogic } from 'kea-test-utils' -import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { membersLogic } from 'scenes/organization/membersLogic' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { teamLogic } from 'scenes/teamLogic' + import { navigationLogic } from '~/layout/navigation/navigationLogic' import { initKeaTests } from '~/test/init' + import { activationLogic } from './activationLogic' describe('activationLogic', () => { diff --git a/frontend/src/lib/components/ActivationSidebar/activationLogic.ts b/frontend/src/lib/components/ActivationSidebar/activationLogic.ts index 16cf859f6aefc..9d11bceabbcf3 100644 --- a/frontend/src/lib/components/ActivationSidebar/activationLogic.ts +++ b/frontend/src/lib/components/ActivationSidebar/activationLogic.ts @@ -1,19 +1,21 @@ -import { kea, path, actions, selectors, connect, reducers, listeners, events } from 'kea' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import { router, urlToAction } from 'kea-router' import api from 'lib/api' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { inviteLogic } from 'scenes/settings/organization/inviteLogic' +import { permanentlyMount } from 'lib/utils/kea-logic-builders' import { membersLogic } from 'scenes/organization/membersLogic' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' +import { inviteLogic } from 'scenes/settings/organization/inviteLogic' import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + import { navigationLogic } from '~/layout/navigation/navigationLogic' +import { dashboardsModel } from '~/models/dashboardsModel' import { EventDefinitionType, ProductKey, TeamBasicType } from '~/types' + import type { activationLogicType } from './activationLogicType' -import { urls } from 'scenes/urls' -import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { dashboardsModel } from '~/models/dashboardsModel' -import { permanentlyMount } from 'lib/utils/kea-logic-builders' export enum ActivationTasks { IngestFirstEvent = 'ingest_first_event', diff --git a/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx b/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx index 16739c8fa9824..c15951310c0db 100644 --- a/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx +++ b/frontend/src/lib/components/ActivityLog/ActivityLog.stories.tsx @@ -1,17 +1,18 @@ +import { Meta } from '@storybook/react' import { featureFlagsActivityResponseJson, insightsActivityResponseJson, personActivityResponseJson, } from 'lib/components/ActivityLog/__mocks__/activityLogMocks' -import { mswDecorator } from '~/mocks/browser' -import { Meta } from '@storybook/react' import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { mswDecorator } from '~/mocks/browser' + const meta: Meta = { title: 'Components/ActivityLog', component: ActivityLog, - parameters: { testOptions: { skip: true } }, // FIXME: Currently disabled as the Timeout story is flaky + tags: ['test-skip'], // FIXME: Currently disabled as the Timeout story is flaky decorators: [ mswDecorator({ get: { diff --git a/frontend/src/lib/components/ActivityLog/ActivityLog.tsx b/frontend/src/lib/components/ActivityLog/ActivityLog.tsx index 17c8c06a1ed40..336f831dd0ea0 100644 --- a/frontend/src/lib/components/ActivityLog/ActivityLog.tsx +++ b/frontend/src/lib/components/ActivityLog/ActivityLog.tsx @@ -1,15 +1,18 @@ -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { TZLabel } from 'lib/components/TZLabel' -import { useValues } from 'kea' import './ActivityLog.scss' -import { ActivityLogLogicProps, activityLogLogic } from 'lib/components/ActivityLog/activityLogLogic' + +import { LemonDivider } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { useValues } from 'kea' +import { activityLogLogic, ActivityLogLogicProps } from 'lib/components/ActivityLog/activityLogLogic' import { HumanizedActivityLogItem } from 'lib/components/ActivityLog/humanizeActivity' -import { PaginationControl, usePagination } from 'lib/lemon-ui/PaginationControl' +import { TZLabel } from 'lib/components/TZLabel' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import clsx from 'clsx' -import { ProductIntroduction } from '../ProductIntroduction/ProductIntroduction' +import { PaginationControl, usePagination } from 'lib/lemon-ui/PaginationControl' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' + import { ProductKey } from '~/types' -import { LemonDivider } from '@posthog/lemon-ui' + +import { ProductIntroduction } from '../ProductIntroduction/ProductIntroduction' export type ActivityLogProps = ActivityLogLogicProps & { startingPage?: number diff --git a/frontend/src/lib/components/ActivityLog/SentenceList.stories.tsx b/frontend/src/lib/components/ActivityLog/SentenceList.stories.tsx index 665460d2fbc5b..a0e29d01d1bef 100644 --- a/frontend/src/lib/components/ActivityLog/SentenceList.stories.tsx +++ b/frontend/src/lib/components/ActivityLog/SentenceList.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { SentenceList, SentenceListProps } from './SentenceList' type Story = StoryObj diff --git a/frontend/src/lib/components/ActivityLog/__mocks__/activityLogMocks.ts b/frontend/src/lib/components/ActivityLog/__mocks__/activityLogMocks.ts index 25a51bb284628..1e5502d8e8766 100644 --- a/frontend/src/lib/components/ActivityLog/__mocks__/activityLogMocks.ts +++ b/frontend/src/lib/components/ActivityLog/__mocks__/activityLogMocks.ts @@ -1,4 +1,5 @@ import { ActivityLogItem, ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' + import { InsightShortId } from '~/types' export const featureFlagsActivityResponseJson: ActivityLogItem[] = [ diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.feature-flag.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.feature-flag.test.tsx index 151e3416219d3..df0cc4c530029 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.feature-flag.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.feature-flag.test.tsx @@ -1,7 +1,9 @@ -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { render } from '@testing-library/react' import '@testing-library/jest-dom' + +import { render } from '@testing-library/react' import { MOCK_TEAM_ID } from 'lib/api.mock' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' + import { makeTestSetup } from './activityLogLogic.test.setup' describe('the activity log logic', () => { diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx index 518eee2fedf77..12d7e59adc172 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.insight.test.tsx @@ -1,8 +1,9 @@ -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { render } from '@testing-library/react' import '@testing-library/jest-dom' + +import { render } from '@testing-library/react' import { MOCK_TEAM_ID } from 'lib/api.mock' import { makeTestSetup } from 'lib/components/ActivityLog/activityLogLogic.test.setup' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' jest.mock('lib/colors') diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.notebook.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.notebook.test.tsx index c3558523f195b..e0f884a96c2d4 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.notebook.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.notebook.test.tsx @@ -1,8 +1,10 @@ -import { ActivityLogItem, ActivityScope, humanize } from 'lib/components/ActivityLog/humanizeActivity' import '@testing-library/jest-dom' -import { InsightShortId } from '~/types' -import { describerFor } from 'lib/components/ActivityLog/activityLogLogic' + import { render } from '@testing-library/react' +import { describerFor } from 'lib/components/ActivityLog/activityLogLogic' +import { ActivityLogItem, ActivityScope, humanize } from 'lib/components/ActivityLog/humanizeActivity' + +import { InsightShortId } from '~/types' describe('the activity log logic', () => { describe('humanizing notebooks', () => { diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx index e63333d6126a9..62d58e6a90624 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.person.test.tsx @@ -1,8 +1,9 @@ -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { render } from '@testing-library/react' import '@testing-library/jest-dom' -import { makeTestSetup } from 'lib/components/ActivityLog/activityLogLogic.test.setup' + +import { render } from '@testing-library/react' import { MOCK_TEAM_ID } from 'lib/api.mock' +import { makeTestSetup } from 'lib/components/ActivityLog/activityLogLogic.test.setup' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' describe('the activity log logic', () => { describe('humanizing persons', () => { diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.plugin.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.plugin.test.tsx index 345f7adb3f3a1..5533218dfd758 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.plugin.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.plugin.test.tsx @@ -1,7 +1,8 @@ -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { render } from '@testing-library/react' import '@testing-library/jest-dom' + +import { render } from '@testing-library/react' import { makeTestSetup } from 'lib/components/ActivityLog/activityLogLogic.test.setup' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' describe('the activity log logic', () => { describe('humanizing plugins', () => { diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.test.setup.ts b/frontend/src/lib/components/ActivityLog/activityLogLogic.test.setup.ts index 42e23c2fc2da5..e947489ac1369 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.test.setup.ts +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.test.setup.ts @@ -1,3 +1,5 @@ +import { expectLogic } from 'kea-test-utils' +import { activityLogLogic } from 'lib/components/ActivityLog/activityLogLogic' import { ActivityChange, ActivityLogItem, @@ -5,10 +7,9 @@ import { PersonMerge, Trigger, } from 'lib/components/ActivityLog/humanizeActivity' + import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' -import { activityLogLogic } from 'lib/components/ActivityLog/activityLogLogic' -import { expectLogic } from 'kea-test-utils' interface APIMockSetup { name: string diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.test.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.test.tsx index 5d8d0179e7d51..762412c406e48 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.test.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.test.tsx @@ -1,12 +1,14 @@ -import { initKeaTests } from '~/test/init' +import '@testing-library/jest-dom' + import { expectLogic } from 'kea-test-utils' -import { useMocks } from '~/mocks/jest' -import { ActivityLogItem, ActivityScope, humanize } from 'lib/components/ActivityLog/humanizeActivity' -import { activityLogLogic, describerFor } from 'lib/components/ActivityLog/activityLogLogic' +import { MOCK_TEAM_ID } from 'lib/api.mock' import { featureFlagsActivityResponseJson } from 'lib/components/ActivityLog/__mocks__/activityLogMocks' +import { activityLogLogic, describerFor } from 'lib/components/ActivityLog/activityLogLogic' +import { ActivityLogItem, ActivityScope, humanize } from 'lib/components/ActivityLog/humanizeActivity' import { flagActivityDescriber } from 'scenes/feature-flags/activityDescriptions' -import '@testing-library/jest-dom' -import { MOCK_TEAM_ID } from 'lib/api.mock' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' describe('the activity log logic', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/ActivityLog/activityLogLogic.tsx b/frontend/src/lib/components/ActivityLog/activityLogLogic.tsx index 4e19411f067bb..e971c14f916e4 100644 --- a/frontend/src/lib/components/ActivityLog/activityLogLogic.tsx +++ b/frontend/src/lib/components/ActivityLog/activityLogLogic.tsx @@ -1,5 +1,6 @@ +import { actions, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, actions, reducers, selectors, listeners, events } from 'kea' +import { router, urlToAction } from 'kea-router' import api, { ActivityLogPaginatedResponse } from 'lib/api' import { ActivityLogItem, @@ -8,18 +9,17 @@ import { humanize, HumanizedActivityLogItem, } from 'lib/components/ActivityLog/humanizeActivity' - -import type { activityLogLogicType } from './activityLogLogicType' +import { ACTIVITY_PAGE_SIZE } from 'lib/constants' import { PaginationManual } from 'lib/lemon-ui/PaginationControl' -import { urls } from 'scenes/urls' -import { router, urlToAction } from 'kea-router' +import { dataManagementActivityDescriber } from 'scenes/data-management/dataManagementDescribers' import { flagActivityDescriber } from 'scenes/feature-flags/activityDescriptions' +import { notebookActivityDescriber } from 'scenes/notebooks/Notebook/notebookActivityDescriber' +import { personActivityDescriber } from 'scenes/persons/activityDescriptions' import { pluginActivityDescriber } from 'scenes/plugins/pluginActivityDescriptions' import { insightActivityDescriber } from 'scenes/saved-insights/activityDescriptions' -import { personActivityDescriber } from 'scenes/persons/activityDescriptions' -import { dataManagementActivityDescriber } from 'scenes/data-management/dataManagementDescribers' -import { notebookActivityDescriber } from 'scenes/notebooks/Notebook/notebookActivityDescriber' -import { ACTIVITY_PAGE_SIZE } from 'lib/constants' +import { urls } from 'scenes/urls' + +import type { activityLogLogicType } from './activityLogLogicType' /** * Having this function inside the `humanizeActivity module was causing very weird test errors in other modules diff --git a/frontend/src/lib/components/ActivityLog/humanizeActivity.tsx b/frontend/src/lib/components/ActivityLog/humanizeActivity.tsx index f7a4c42e1b0af..cd8ef8a39e0fd 100644 --- a/frontend/src/lib/components/ActivityLog/humanizeActivity.tsx +++ b/frontend/src/lib/components/ActivityLog/humanizeActivity.tsx @@ -1,4 +1,5 @@ import { dayjs } from 'lib/dayjs' + import { InsightShortId, PersonType } from '~/types' export interface ActivityChange { diff --git a/frontend/src/lib/components/AddToDashboard/AddToDashboard.tsx b/frontend/src/lib/components/AddToDashboard/AddToDashboard.tsx index a90631adf8f83..20e791542db48 100644 --- a/frontend/src/lib/components/AddToDashboard/AddToDashboard.tsx +++ b/frontend/src/lib/components/AddToDashboard/AddToDashboard.tsx @@ -1,8 +1,9 @@ -import { InsightModel } from '~/types' -import { dashboardsModel } from '~/models/dashboardsModel' import { useValues } from 'kea' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconGauge, IconWithCount } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { InsightModel } from '~/types' interface SaveToDashboardProps { insight: Partial diff --git a/frontend/src/lib/components/AddToDashboard/AddToDashboardModal.tsx b/frontend/src/lib/components/AddToDashboard/AddToDashboardModal.tsx index 40303016da557..ea45c93d16fe1 100644 --- a/frontend/src/lib/components/AddToDashboard/AddToDashboardModal.tsx +++ b/frontend/src/lib/components/AddToDashboard/AddToDashboardModal.tsx @@ -1,19 +1,20 @@ -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import clsx from 'clsx' import { useActions, useValues } from 'kea' import { addToDashboardModalLogic } from 'lib/components/AddToDashboard/addToDashboardModalLogic' -import { urls } from 'scenes/urls' import { IconCottage } from 'lib/lemon-ui/icons' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { List, ListRowProps, ListRowRenderer } from 'react-virtualized/dist/es/List' -import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonModal } from 'lib/lemon-ui/LemonModal' import { Link } from 'lib/lemon-ui/Link' -import { DashboardBasicType, InsightModel } from '~/types' -import clsx from 'clsx' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { pluralize } from 'lib/utils' -import { teamLogic } from 'scenes/teamLogic' -import { LemonModal } from 'lib/lemon-ui/LemonModal' import { CSSProperties } from 'react' +import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' +import { List, ListRowProps, ListRowRenderer } from 'react-virtualized/dist/es/List' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { DashboardBasicType, InsightModel } from '~/types' interface SaveToDashboardModalProps { isOpen: boolean diff --git a/frontend/src/lib/components/AddToDashboard/addToDashboardModalLogic.ts b/frontend/src/lib/components/AddToDashboard/addToDashboardModalLogic.ts index c6e363e9fa415..c3d333e2acd47 100644 --- a/frontend/src/lib/components/AddToDashboard/addToDashboardModalLogic.ts +++ b/frontend/src/lib/components/AddToDashboard/addToDashboardModalLogic.ts @@ -1,13 +1,14 @@ -import { kea, props, key, path, connect, actions, reducers, selectors, listeners } from 'kea' -import { dashboardsModel } from '~/models/dashboardsModel' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' -import { DashboardBasicType, DashboardType, InsightModel, InsightType } from '~/types' import FuseClass from 'fuse.js' -import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' import { insightLogic } from 'scenes/insights/insightLogic' +import { urls } from 'scenes/urls' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { DashboardBasicType, DashboardType, InsightModel, InsightType } from '~/types' import type { addToDashboardModalLogicType } from './addToDashboardModalLogicType' diff --git a/frontend/src/lib/components/Animation/Animation.stories.tsx b/frontend/src/lib/components/Animation/Animation.stories.tsx index aa253dda4377f..cd62f7369b638 100644 --- a/frontend/src/lib/components/Animation/Animation.stories.tsx +++ b/frontend/src/lib/components/Animation/Animation.stories.tsx @@ -1,5 +1,5 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' import { AnimationType } from 'lib/animations/animations' -import { StoryFn, Meta, StoryObj } from '@storybook/react' import { Animation } from 'lib/components/Animation/Animation' type Story = StoryObj @@ -12,7 +12,6 @@ const meta: Meta = { 'Animations are [LottieFiles.com](https://lottiefiles.com/) animations that we load asynchronously.', }, }, - testOptions: { skip: true }, // Animations aren't particularly snapshotable }, argTypes: { size: { @@ -25,7 +24,7 @@ const meta: Meta = { control: { type: 'radio' }, }, }, - tags: ['autodocs'], + tags: ['autodocs', 'test-skip'], // Animations aren't particularly snapshotable } export default meta diff --git a/frontend/src/lib/components/Animation/Animation.tsx b/frontend/src/lib/components/Animation/Animation.tsx index 6852c4f57ce29..df6fbadc929c7 100644 --- a/frontend/src/lib/components/Animation/Animation.tsx +++ b/frontend/src/lib/components/Animation/Animation.tsx @@ -1,9 +1,10 @@ import './Animation.scss' + import { Player } from '@lottiefiles/react-lottie-player' -import { useEffect, useState } from 'react' import clsx from 'clsx' -import { AnimationType, getAnimationSource, animations } from 'lib/animations/animations' +import { animations, AnimationType, getAnimationSource } from 'lib/animations/animations' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { useEffect, useState } from 'react' export interface AnimationProps { /** Animation to show */ diff --git a/frontend/src/lib/components/AnnotationsOverlay/AnnotationsOverlay.tsx b/frontend/src/lib/components/AnnotationsOverlay/AnnotationsOverlay.tsx index 03dca71fd5bf1..3f43c79ca46d0 100644 --- a/frontend/src/lib/components/AnnotationsOverlay/AnnotationsOverlay.tsx +++ b/frontend/src/lib/components/AnnotationsOverlay/AnnotationsOverlay.tsx @@ -1,26 +1,29 @@ +import './AnnotationsOverlay.scss' + +import { Chart } from 'chart.js' import { BindLogic, useActions, useValues } from 'kea' import { dayjs } from 'lib/dayjs' -import { humanFriendlyDetailedTime, pluralize, shortTimeZone } from 'lib/utils' -import React, { useRef, useState } from 'react' -import { IntervalType, AnnotationType } from '~/types' import { IconDelete, IconEdit, IconPlusMini } from 'lib/lemon-ui/icons' import { LemonBadge } from 'lib/lemon-ui/LemonBadge/LemonBadge' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { humanFriendlyDetailedTime, pluralize, shortTimeZone } from 'lib/utils' +import React, { useRef, useState } from 'react' +import { AnnotationModal } from 'scenes/annotations/AnnotationModal' +import { annotationModalLogic, annotationScopeToName } from 'scenes/annotations/annotationModalLogic' +import { insightLogic } from 'scenes/insights/insightLogic' + +import { annotationsModel } from '~/models/annotationsModel' +import { AnnotationType, IntervalType } from '~/types' + import { annotationsOverlayLogic, AnnotationsOverlayLogicProps, determineAnnotationsDateGroup, } from './annotationsOverlayLogic' -import './AnnotationsOverlay.scss' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { AnnotationModal } from 'scenes/annotations/AnnotationModal' -import { annotationModalLogic, annotationScopeToName } from 'scenes/annotations/annotationModalLogic' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { annotationsModel } from '~/models/annotationsModel' -import { Chart } from 'chart.js' import { useAnnotationsPositioning } from './useAnnotationsPositioning' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { insightLogic } from 'scenes/insights/insightLogic' /** User-facing format for annotation groups. */ const INTERVAL_UNIT_TO_HUMAN_DAYJS_FORMAT: Record = { diff --git a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts index 8e98373a9a201..02d59125c852f 100644 --- a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts +++ b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.test.ts @@ -1,10 +1,12 @@ import { expectLogic } from 'kea-test-utils' import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' import { insightLogic } from 'scenes/insights/insightLogic' + import { useMocks } from '~/mocks/jest' import { annotationsModel, deserializeAnnotation } from '~/models/annotationsModel' import { initKeaTests } from '~/test/init' -import { AnnotationScope, RawAnnotationType, InsightShortId, IntervalType, AnnotationType } from '~/types' +import { AnnotationScope, AnnotationType, InsightShortId, IntervalType, RawAnnotationType } from '~/types' + import { annotationsOverlayLogic } from './annotationsOverlayLogic' jest.spyOn(Storage.prototype, 'getItem') diff --git a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.ts b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.ts index 2411ba3614c24..881beaf6e3590 100644 --- a/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.ts +++ b/frontend/src/lib/components/AnnotationsOverlay/annotationsOverlayLogic.ts @@ -1,12 +1,14 @@ +import { Tick } from 'chart.js' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { Dayjs, dayjsLocalToTimezone } from 'lib/dayjs' -import { kea, path, selectors, key, props, connect, listeners, actions, reducers } from 'kea' import { groupBy } from 'lib/utils' -import { AnnotationScope, DatedAnnotationType, InsightLogicProps, InsightModel, IntervalType } from '~/types' -import type { annotationsOverlayLogicType } from './annotationsOverlayLogicType' import { insightLogic } from 'scenes/insights/insightLogic' -import { AnnotationDataWithoutInsight, annotationsModel } from '~/models/annotationsModel' import { teamLogic } from 'scenes/teamLogic' -import { Tick } from 'chart.js' + +import { AnnotationDataWithoutInsight, annotationsModel } from '~/models/annotationsModel' +import { AnnotationScope, DatedAnnotationType, InsightLogicProps, InsightModel, IntervalType } from '~/types' + +import type { annotationsOverlayLogicType } from './annotationsOverlayLogicType' export interface AnnotationsOverlayLogicProps extends InsightLogicProps { insightNumericId: InsightModel['id'] | 'new' diff --git a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx index e6f02e1239eb2..29fa6415988e1 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx +++ b/frontend/src/lib/components/AuthorizedUrlList/AuthorizedUrlList.tsx @@ -1,14 +1,15 @@ import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { AuthorizedUrlListType as AuthorizedUrlListType, authorizedUrlListLogic } from './authorizedUrlListLogic' -import { IconDelete, IconEdit, IconOpenInApp, IconPlus } from 'lib/lemon-ui/icons' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { Form } from 'kea-forms' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { Field } from 'lib/forms/Field' +import { IconDelete, IconEdit, IconOpenInApp, IconPlus } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' + +import { authorizedUrlListLogic, AuthorizedUrlListType as AuthorizedUrlListType } from './authorizedUrlListLogic' function EmptyState({ numberOfResults, diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts index 68a5209ee37f9..b21f9012925bb 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts +++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.test.ts @@ -1,3 +1,11 @@ +import { router } from 'kea-router' +import { expectLogic } from 'kea-test-utils' +import { api, MOCK_TEAM_ID } from 'lib/api.mock' +import { urls } from 'scenes/urls' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' + import { appEditorUrl, authorizedUrlListLogic, @@ -5,12 +13,6 @@ import { filterNotAuthorizedUrls, validateProposedUrl, } from './authorizedUrlListLogic' -import { initKeaTests } from '~/test/init' -import { router } from 'kea-router' -import { expectLogic } from 'kea-test-utils' -import { useMocks } from '~/mocks/jest' -import { urls } from 'scenes/urls' -import { api, MOCK_TEAM_ID } from 'lib/api.mock' describe('the authorized urls list logic', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts index c45561eef0882..652d357130452 100644 --- a/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts +++ b/frontend/src/lib/components/AuthorizedUrlList/authorizedUrlListLogic.ts @@ -1,3 +1,4 @@ +import Fuse from 'fuse.js' import { actions, afterMount, @@ -11,20 +12,20 @@ import { selectors, sharedListeners, } from 'kea' +import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' +import { encodeParams, urlToAction } from 'kea-router' +import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' import { isDomain, isURL } from 'lib/utils' -import { ToolbarParams } from '~/types' import { teamLogic } from 'scenes/teamLogic' -import Fuse from 'fuse.js' -import { encodeParams, urlToAction } from 'kea-router' import { urls } from 'scenes/urls' -import { loaders } from 'kea-loaders' -import { forms } from 'kea-forms' -import type { authorizedUrlListLogicType } from './authorizedUrlListLogicType' -import { subscriptions } from 'kea-subscriptions' import { HogQLQuery, NodeKind } from '~/queries/schema' import { hogql } from '~/queries/utils' +import { ToolbarParams } from '~/types' + +import type { authorizedUrlListLogicType } from './authorizedUrlListLogicType' export interface ProposeNewUrlFormType { url: string diff --git a/frontend/src/lib/components/BillingAlertsV2.tsx b/frontend/src/lib/components/BillingAlertsV2.tsx index 21a4023a6262a..0e9c84c4443c3 100644 --- a/frontend/src/lib/components/BillingAlertsV2.tsx +++ b/frontend/src/lib/components/BillingAlertsV2.tsx @@ -1,9 +1,9 @@ import { useActions, useValues } from 'kea' import { router } from 'kea-router' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { useEffect, useState } from 'react' import { billingLogic } from 'scenes/billing/billingLogic' import { urls } from 'scenes/urls' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' export function BillingAlertsV2(): JSX.Element | null { const { billingAlert } = useValues(billingLogic) @@ -12,18 +12,26 @@ export function BillingAlertsV2(): JSX.Element | null { const [alertHidden, setAlertHidden] = useState(false) useEffect(() => { + if (billingAlert?.pathName && currentLocation.pathname !== billingAlert?.pathName) { + setAlertHidden(true) + } else { + setAlertHidden(false) + } if (billingAlert) { reportBillingAlertShown(billingAlert) } - }, [billingAlert]) + }, [billingAlert, currentLocation]) if (!billingAlert || alertHidden) { return null } - const showButton = billingAlert.contactSupport || currentLocation.pathname !== urls.organizationBilling() + const showButton = + billingAlert.action || billingAlert.contactSupport || currentLocation.pathname !== urls.organizationBilling() - const buttonProps = billingAlert.contactSupport + const buttonProps = billingAlert.action + ? billingAlert.action + : billingAlert.contactSupport ? { to: 'mailto:sales@posthog.com', children: billingAlert.buttonCTA || 'Contact support', diff --git a/frontend/src/lib/components/BridgePage/BridgePage.scss b/frontend/src/lib/components/BridgePage/BridgePage.scss index fca0fc8ef9599..fb780e9cfe9c4 100644 --- a/frontend/src/lib/components/BridgePage/BridgePage.scss +++ b/frontend/src/lib/components/BridgePage/BridgePage.scss @@ -6,7 +6,9 @@ display: flex; flex-direction: column; flex: 1; - overflow: scroll; + overflow: hidden; + min-height: 100vh; + height: 100%; &::-webkit-scrollbar { width: 0 !important; diff --git a/frontend/src/lib/components/BridgePage/BridgePage.tsx b/frontend/src/lib/components/BridgePage/BridgePage.tsx index d7c270d0f5fe1..dce4e1df9fd54 100644 --- a/frontend/src/lib/components/BridgePage/BridgePage.tsx +++ b/frontend/src/lib/components/BridgePage/BridgePage.tsx @@ -1,15 +1,17 @@ +import './BridgePage.scss' + import clsx from 'clsx' +import { useValues } from 'kea' import { useEffect, useState } from 'react' -import { WelcomeLogo } from 'scenes/authentication/WelcomeLogo' import { CSSTransition } from 'react-transition-group' -import './BridgePage.scss' -import { LaptopHog4, LaptopHogEU } from '../hedgehogs' -import { useValues } from 'kea' +import { WelcomeLogo } from 'scenes/authentication/WelcomeLogo' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { Region } from '~/types' +import { LaptopHog4, LaptopHogEU } from '../hedgehogs' + export type BridgePageCommonProps = { - className?: string children?: React.ReactNode footer?: React.ReactNode header?: React.ReactNode @@ -18,7 +20,6 @@ export type BridgePageCommonProps = { sideLogo?: boolean fixedWidth?: boolean leftContainerContent?: JSX.Element - fullScreen?: boolean } interface NoHedgehogProps extends BridgePageCommonProps { @@ -36,7 +37,6 @@ type BridgePageProps = NoHedgehogProps | YesHedgehogProps export function BridgePage({ children, - className, header, footer, view, @@ -46,7 +46,6 @@ export function BridgePage({ fixedWidth = true, leftContainerContent, hedgehog = false, - fullScreen = true, }: BridgePageProps): JSX.Element { const [messageShowing, setMessageShowing] = useState(false) const { preflight } = useValues(preflightLogic) @@ -59,14 +58,7 @@ export function BridgePage({ }, []) return ( -
    +
    {leftContainerContent || hedgehog ? (
    @@ -108,7 +100,7 @@ export function BridgePage({
    {children}
    -
    {footer}
    + {footer &&
    {footer}
    }
    ) } diff --git a/frontend/src/lib/components/Cards/CardMeta.tsx b/frontend/src/lib/components/Cards/CardMeta.tsx index 4cb6b83e6a129..c6b9bc50d8610 100644 --- a/frontend/src/lib/components/Cards/CardMeta.tsx +++ b/frontend/src/lib/components/Cards/CardMeta.tsx @@ -1,12 +1,14 @@ +import './CardMeta.scss' + import clsx from 'clsx' import { useResizeObserver } from 'lib/hooks/useResizeObserver' -import { InsightColor } from '~/types' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconSubtitles, IconSubtitlesOff } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { More } from 'lib/lemon-ui/LemonButton/More' -import './CardMeta.scss' import { Transition } from 'react-transition-group' +import { InsightColor } from '~/types' + export interface Resizeable { showResizeHandles?: boolean canResizeWidth?: boolean diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightCard.stories.tsx b/frontend/src/lib/components/Cards/InsightCard/InsightCard.stories.tsx index 15224b0c3d747..9e36ec21d1f87 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightCard.stories.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/InsightCard.stories.tsx @@ -1,21 +1,22 @@ import { Meta, Story } from '@storybook/react' import { useState } from 'react' + import { ChartDisplayType, InsightColor, InsightModel, InsightShortId, TrendsFilterType } from '~/types' -import { InsightCard as InsightCardComponent } from './index' +import EXAMPLE_DATA_TABLE_NODE_EVENTS_QUERY from '../../../../mocks/fixtures/api/projects/team_id/insights/dataTableEvents.json' +import EXAMPLE_DATA_TABLE_NODE_HOGQL_QUERY from '../../../../mocks/fixtures/api/projects/team_id/insights/dataTableHogQL.json' +import EXAMPLE_FUNNEL from '../../../../mocks/fixtures/api/projects/team_id/insights/funnelLeftToRight.json' +import EXAMPLE_LIFECYCLE from '../../../../mocks/fixtures/api/projects/team_id/insights/lifecycle.json' +import EXAMPLE_RETENTION from '../../../../mocks/fixtures/api/projects/team_id/insights/retention.json' +import EXAMPLE_STICKINESS from '../../../../mocks/fixtures/api/projects/team_id/insights/stickiness.json' import EXAMPLE_TRENDS from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsLine.json' import EXAMPLE_TRENDS_MULTI from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsLineMulti.json' -import EXAMPLE_TRENDS_HORIZONTAL_BAR from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsValue.json' -import EXAMPLE_TRENDS_TABLE from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsTable.json' import EXAMPLE_TRENDS_PIE from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsPie.json' +import EXAMPLE_TRENDS_TABLE from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsTable.json' +import EXAMPLE_TRENDS_HORIZONTAL_BAR from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsValue.json' import EXAMPLE_TRENDS_WORLD_MAP from '../../../../mocks/fixtures/api/projects/team_id/insights/trendsWorldMap.json' -import EXAMPLE_FUNNEL from '../../../../mocks/fixtures/api/projects/team_id/insights/funnelLeftToRight.json' -import EXAMPLE_RETENTION from '../../../../mocks/fixtures/api/projects/team_id/insights/retention.json' import EXAMPLE_PATHS from '../../../../mocks/fixtures/api/projects/team_id/insights/userPaths.json' -import EXAMPLE_STICKINESS from '../../../../mocks/fixtures/api/projects/team_id/insights/stickiness.json' -import EXAMPLE_LIFECYCLE from '../../../../mocks/fixtures/api/projects/team_id/insights/lifecycle.json' -import EXAMPLE_DATA_TABLE_NODE_HOGQL_QUERY from '../../../../mocks/fixtures/api/projects/team_id/insights/dataTableHogQL.json' -import EXAMPLE_DATA_TABLE_NODE_EVENTS_QUERY from '../../../../mocks/fixtures/api/projects/team_id/insights/dataTableEvents.json' +import { InsightCard as InsightCardComponent } from './index' const examples = [ EXAMPLE_TRENDS, diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightCard.tsx b/frontend/src/lib/components/Cards/InsightCard/InsightCard.tsx index a66f87cad040a..eacf36fb137eb 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightCard.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/InsightCard.tsx @@ -1,7 +1,15 @@ +import './InsightCard.scss' + import clsx from 'clsx' import { BindLogic, useValues } from 'kea' +import { Resizeable } from 'lib/components/Cards/CardMeta' +import { QueriesUnsupportedHere } from 'lib/components/Cards/InsightCard/QueriesUnsupportedHere' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import React, { useState } from 'react' import { Layout } from 'react-grid-layout' +import { Funnel } from 'scenes/funnels/Funnel' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' import { FunnelInvalidExclusionState, FunnelSingleStepState, @@ -9,7 +17,24 @@ import { InsightErrorState, InsightTimeoutState, } from 'scenes/insights/EmptyStates' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { isFilterWithDisplay, isFunnelsFilter, isPathsFilter, isRetentionFilter } from 'scenes/insights/sharedUtils' +import { BoldNumber } from 'scenes/insights/views/BoldNumber' +import { DashboardInsightsTable } from 'scenes/insights/views/InsightsTable/DashboardInsightsTable' +import { WorldMap } from 'scenes/insights/views/WorldMap' +import { Paths } from 'scenes/paths/Paths' +import { RetentionContainer } from 'scenes/retention/RetentionContainer' +import { ActionsHorizontalBar, ActionsLineGraph, ActionsPie } from 'scenes/trends/viz' + +import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' +import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' +import { getCachedResults } from '~/queries/nodes/InsightViz/utils' +import { Query } from '~/queries/Query/Query' +import { InsightQueryNode } from '~/queries/schema' +import { QueryContext } from '~/queries/types' import { ChartDisplayType, ChartParams, @@ -23,33 +48,9 @@ import { InsightModel, InsightType, } from '~/types' -import { ResizeHandle1D, ResizeHandle2D } from '../handles' -import './InsightCard.scss' -import { ActionsHorizontalBar, ActionsLineGraph, ActionsPie } from 'scenes/trends/viz' -import { DashboardInsightsTable } from 'scenes/insights/views/InsightsTable/DashboardInsightsTable' -import { Funnel } from 'scenes/funnels/Funnel' -import { RetentionContainer } from 'scenes/retention/RetentionContainer' -import { Paths } from 'scenes/paths/Paths' - -import { WorldMap } from 'scenes/insights/views/WorldMap' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { BoldNumber } from 'scenes/insights/views/BoldNumber' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { isFilterWithDisplay, isFunnelsFilter, isPathsFilter, isRetentionFilter } from 'scenes/insights/sharedUtils' -import { Resizeable } from 'lib/components/Cards/CardMeta' -import { Query } from '~/queries/Query/Query' -import { QueriesUnsupportedHere } from 'lib/components/Cards/InsightCard/QueriesUnsupportedHere' -import { InsightQueryNode } from '~/queries/schema' -import { QueryContext } from '~/queries/types' +import { ResizeHandle1D, ResizeHandle2D } from '../handles' import { InsightMeta } from './InsightMeta' -import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' -import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' -import { getCachedResults } from '~/queries/nodes/InsightViz/utils' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' type DisplayedType = ChartDisplayType | 'RetentionContainer' | 'FunnelContainer' | 'PathsContainer' diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightDetails.tsx b/frontend/src/lib/components/Cards/InsightCard/InsightDetails.tsx index 35198f96ed277..fe1c16d3cd37d 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightDetails.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/InsightDetails.tsx @@ -1,9 +1,29 @@ import { useValues } from 'kea' +import { + formatPropertyLabel, + isAnyPropertyfilter, + isCohortPropertyFilter, + isPropertyFilterWithOperator, +} from 'lib/components/PropertyFilters/utils' +import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { IconCalculate, IconSubdirectoryArrowRight } from 'lib/lemon-ui/icons' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { Link } from 'lib/lemon-ui/Link' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { KEY_MAPPING } from 'lib/taxonomy' import { allOperatorsMapping, capitalizeFirstLetter } from 'lib/utils' +import React from 'react' import { LocalFilter, toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' +import { BreakdownTag } from 'scenes/insights/filters/BreakdownFilter/BreakdownTag' +import { isPathsFilter, isTrendsFilter } from 'scenes/insights/sharedUtils' import { humanizePathsEventTypes } from 'scenes/insights/utils' import { apiValueToMathType, MathCategory, MathDefinition, mathsLogic } from 'scenes/trends/mathsLogic' import { urls } from 'scenes/urls' + +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { filterForQuery, isInsightQueryNode } from '~/queries/utils' import { FilterLogicalOperator, FilterType, @@ -12,27 +32,9 @@ import { PathsFilterType, PropertyGroupFilter, } from '~/types' -import { IconCalculate, IconSubdirectoryArrowRight } from 'lib/lemon-ui/icons' -import { LemonRow } from 'lib/lemon-ui/LemonRow' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { SeriesLetter } from 'lib/components/SeriesGlyph' -import { Link } from 'lib/lemon-ui/Link' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' + import { PropertyKeyInfo } from '../../PropertyKeyInfo' -import { KEY_MAPPING } from 'lib/taxonomy' import { TZLabel } from '../../TZLabel' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { cohortsModel } from '~/models/cohortsModel' -import React from 'react' -import { isPathsFilter, isTrendsFilter } from 'scenes/insights/sharedUtils' -import { - formatPropertyLabel, - isAnyPropertyfilter, - isCohortPropertyFilter, - isPropertyFilterWithOperator, -} from 'lib/components/PropertyFilters/utils' -import { filterForQuery, isInsightQueryNode } from '~/queries/utils' -import { BreakdownTag } from 'scenes/insights/filters/BreakdownFilter/BreakdownTag' function CompactPropertyFiltersDisplay({ groupFilter, diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx b/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx index 2b85413419f02..fab06e2401974 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/InsightMeta.tsx @@ -1,29 +1,31 @@ +// eslint-disable-next-line no-restricted-imports +import { PieChartFilled } from '@ant-design/icons' import { useActions, useValues } from 'kea' +import { CardMeta } from 'lib/components/Cards/CardMeta' +import { TopHeading } from 'lib/components/Cards/InsightCard/TopHeading' +import { ExportButton } from 'lib/components/ExportButton/ExportButton' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { DashboardPrivilegeLevel } from 'lib/constants' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { Link } from 'lib/lemon-ui/Link' +import { Splotch, SplotchColor } from 'lib/lemon-ui/Splotch' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { capitalizeFirstLetter } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import React from 'react' import { insightLogic } from 'scenes/insights/insightLogic' +import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { mathsLogic } from 'scenes/trends/mathsLogic' import { urls } from 'scenes/urls' + +import { cohortsModel } from '~/models/cohortsModel' import { dashboardsModel } from '~/models/dashboardsModel' -import { ExporterFormat, InsightColor } from '~/types' -import { Splotch, SplotchColor } from 'lib/lemon-ui/Splotch' -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { Link } from 'lib/lemon-ui/Link' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { InsightDetails } from './InsightDetails' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { groupsModel } from '~/models/groupsModel' -import { cohortsModel } from '~/models/cohortsModel' -import { mathsLogic } from 'scenes/trends/mathsLogic' -import { ExportButton } from 'lib/components/ExportButton/ExportButton' -import { CardMeta } from 'lib/components/Cards/CardMeta' -import { DashboardPrivilegeLevel } from 'lib/constants' -// eslint-disable-next-line no-restricted-imports -import { PieChartFilled } from '@ant-design/icons' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { TopHeading } from 'lib/components/Cards/InsightCard/TopHeading' -import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { ExporterFormat, InsightColor } from '~/types' + import { InsightCardProps } from './InsightCard' +import { InsightDetails } from './InsightDetails' interface InsightMetaProps extends Pick< diff --git a/frontend/src/lib/components/Cards/InsightCard/TopHeading.tsx b/frontend/src/lib/components/Cards/InsightCard/TopHeading.tsx index 731a9c3c8e781..3d059c2a11236 100644 --- a/frontend/src/lib/components/Cards/InsightCard/TopHeading.tsx +++ b/frontend/src/lib/components/Cards/InsightCard/TopHeading.tsx @@ -1,7 +1,8 @@ -import { InsightModel, InsightType } from '~/types' +import { dateFilterToText } from 'lib/utils' import { INSIGHT_TYPES_METADATA, InsightTypeMetadata, QUERY_TYPES_METADATA } from 'scenes/saved-insights/SavedInsights' + import { containsHogQLQuery, dateRangeFor, isDataTableNode, isInsightQueryNode } from '~/queries/utils' -import { dateFilterToText } from 'lib/utils' +import { InsightModel, InsightType } from '~/types' export function TopHeading({ insight }: { insight: InsightModel }): JSX.Element { const { filters, query } = insight diff --git a/frontend/src/lib/components/Cards/TextCard/TextCard.stories.tsx b/frontend/src/lib/components/Cards/TextCard/TextCard.stories.tsx index e6790f8446a6b..c8f1a2d8ee482 100644 --- a/frontend/src/lib/components/Cards/TextCard/TextCard.stories.tsx +++ b/frontend/src/lib/components/Cards/TextCard/TextCard.stories.tsx @@ -1,5 +1,7 @@ import { Meta, Story } from '@storybook/react' + import { DashboardTile, InsightColor } from '~/types' + import { TextCard } from './TextCard' const meta: Meta = { diff --git a/frontend/src/lib/components/Cards/TextCard/TextCard.tsx b/frontend/src/lib/components/Cards/TextCard/TextCard.tsx index 9e6dc2e07c71c..dba5699b07a7e 100644 --- a/frontend/src/lib/components/Cards/TextCard/TextCard.tsx +++ b/frontend/src/lib/components/Cards/TextCard/TextCard.tsx @@ -1,16 +1,18 @@ import './TextCard.scss' -import { ResizeHandle1D, ResizeHandle2D } from 'lib/components/Cards/handles' -import clsx from 'clsx' -import { DashboardBasicType, DashboardTile } from '~/types' + import { LemonButton, LemonButtonWithDropdown, LemonDivider } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import { dashboardsModel } from '~/models/dashboardsModel' import { Resizeable } from 'lib/components/Cards/CardMeta' +import { ResizeHandle1D, ResizeHandle2D } from 'lib/components/Cards/handles' +import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' import React from 'react' -import { More } from 'lib/lemon-ui/LemonButton/More' +import { urls } from 'scenes/urls' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { DashboardBasicType, DashboardTile } from '~/types' interface TextCardProps extends React.HTMLAttributes, Resizeable { dashboardId?: string | number diff --git a/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx b/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx index a29655f96afba..47fffc6cff914 100644 --- a/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx +++ b/frontend/src/lib/components/Cards/TextCard/TextCardModal.tsx @@ -1,13 +1,14 @@ -import { AvailableFeature, DashboardType } from '~/types' -import { textCardModalLogic } from 'lib/components/Cards/TextCard/textCardModalLogic' import { useActions, useValues } from 'kea' -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { Field, Form } from 'kea-forms' -import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { textCardModalLogic } from 'lib/components/Cards/TextCard/textCardModalLogic' import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonTextAreaMarkdown } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' import { userLogic } from 'scenes/userLogic' +import { AvailableFeature, DashboardType } from '~/types' + export function TextCardModal({ isOpen, onClose, diff --git a/frontend/src/lib/components/Cards/TextCard/textCardModalLogic.ts b/frontend/src/lib/components/Cards/TextCard/textCardModalLogic.ts index 3b9a0514526db..15bde2f1a656e 100644 --- a/frontend/src/lib/components/Cards/TextCard/textCardModalLogic.ts +++ b/frontend/src/lib/components/Cards/TextCard/textCardModalLogic.ts @@ -1,6 +1,7 @@ import { lemonToast } from '@posthog/lemon-ui' -import { kea, props, path, connect, key, listeners } from 'kea' +import { connect, kea, key, listeners, path, props } from 'kea' import { forms } from 'kea-forms' + import { dashboardsModel } from '~/models/dashboardsModel' import { DashboardTile, DashboardType } from '~/types' diff --git a/frontend/src/lib/components/ChartFilter/ChartFilter.tsx b/frontend/src/lib/components/ChartFilter/ChartFilter.tsx index 89b5d39673e46..4cac6dcd3cec6 100644 --- a/frontend/src/lib/components/ChartFilter/ChartFilter.tsx +++ b/frontend/src/lib/components/ChartFilter/ChartFilter.tsx @@ -1,20 +1,20 @@ +import { LemonSelect, LemonSelectOptions } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { - IconShowChart, - IconCumulativeChart, - IconBarChart, - IconAreaChart, Icon123, + IconAreaChart, + IconBarChart, + IconCumulativeChart, IconPieChart, - IconTableChart, IconPublic, + IconShowChart, + IconTableChart, } from 'lib/lemon-ui/icons' - -import { ChartDisplayType } from '~/types' import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonSelect, LemonSelectOptions } from '@posthog/lemon-ui' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { ChartDisplayType } from '~/types' + export function ChartFilter(): JSX.Element { const { insightProps } = useValues(insightLogic) const { display } = useValues(insightVizDataLogic(insightProps)) diff --git a/frontend/src/lib/components/CodeEditors.tsx b/frontend/src/lib/components/CodeEditors.tsx index 1088a710d25e2..3fd7c96eecceb 100644 --- a/frontend/src/lib/components/CodeEditors.tsx +++ b/frontend/src/lib/components/CodeEditors.tsx @@ -1,8 +1,9 @@ +import MonacoEditor, { type EditorProps } from '@monaco-editor/react' +import { useValues } from 'kea' import { Spinner } from 'lib/lemon-ui/Spinner' import { inStorybookTestRunner } from 'lib/utils' -import MonacoEditor, { type EditorProps } from '@monaco-editor/react' + import { themeLogic } from '~/layout/navigation-3000/themeLogic' -import { useValues } from 'kea' export type CodeEditorProps = Omit diff --git a/frontend/src/lib/components/CodeSnippet/CodeSnippet.tsx b/frontend/src/lib/components/CodeSnippet/CodeSnippet.tsx index 4b101df7bfa1a..68ae2f5f9a09d 100644 --- a/frontend/src/lib/components/CodeSnippet/CodeSnippet.tsx +++ b/frontend/src/lib/components/CodeSnippet/CodeSnippet.tsx @@ -1,34 +1,35 @@ +import './CodeSnippet.scss' + +import { Popconfirm } from 'antd' +import { PopconfirmProps } from 'antd/lib/popconfirm' +import clsx from 'clsx' +import { useValues } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { IconCopy, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { useState } from 'react' import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter' -import okaidia from 'react-syntax-highlighter/dist/esm/styles/prism/okaidia' -import synthwave84 from 'react-syntax-highlighter/dist/esm/styles/prism/synthwave84' import bash from 'react-syntax-highlighter/dist/esm/languages/prism/bash' -import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx' -import javascript from 'react-syntax-highlighter/dist/esm/languages/prism/javascript' -import java from 'react-syntax-highlighter/dist/esm/languages/prism/java' -import ruby from 'react-syntax-highlighter/dist/esm/languages/prism/ruby' -import objectiveC from 'react-syntax-highlighter/dist/esm/languages/prism/objectivec' -import swift from 'react-syntax-highlighter/dist/esm/languages/prism/swift' -import elixir from 'react-syntax-highlighter/dist/esm/languages/prism/elixir' -import php from 'react-syntax-highlighter/dist/esm/languages/prism/php' -import python from 'react-syntax-highlighter/dist/esm/languages/prism/python' import dart from 'react-syntax-highlighter/dist/esm/languages/prism/dart' +import elixir from 'react-syntax-highlighter/dist/esm/languages/prism/elixir' import go from 'react-syntax-highlighter/dist/esm/languages/prism/go' +import http from 'react-syntax-highlighter/dist/esm/languages/prism/http' +import java from 'react-syntax-highlighter/dist/esm/languages/prism/java' +import javascript from 'react-syntax-highlighter/dist/esm/languages/prism/javascript' import json from 'react-syntax-highlighter/dist/esm/languages/prism/json' -import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml' +import jsx from 'react-syntax-highlighter/dist/esm/languages/prism/jsx' import markup from 'react-syntax-highlighter/dist/esm/languages/prism/markup' -import http from 'react-syntax-highlighter/dist/esm/languages/prism/http' +import objectiveC from 'react-syntax-highlighter/dist/esm/languages/prism/objectivec' +import php from 'react-syntax-highlighter/dist/esm/languages/prism/php' +import python from 'react-syntax-highlighter/dist/esm/languages/prism/python' +import ruby from 'react-syntax-highlighter/dist/esm/languages/prism/ruby' import sql from 'react-syntax-highlighter/dist/esm/languages/prism/sql' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { Popconfirm } from 'antd' -import { PopconfirmProps } from 'antd/lib/popconfirm' -import './CodeSnippet.scss' -import { IconCopy, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { useValues } from 'kea' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { useState } from 'react' -import clsx from 'clsx' +import swift from 'react-syntax-highlighter/dist/esm/languages/prism/swift' +import yaml from 'react-syntax-highlighter/dist/esm/languages/prism/yaml' +import okaidia from 'react-syntax-highlighter/dist/esm/styles/prism/okaidia' +import synthwave84 from 'react-syntax-highlighter/dist/esm/styles/prism/synthwave84' export enum Language { Text = 'text', diff --git a/frontend/src/lib/components/CommandBar/ActionBar.tsx b/frontend/src/lib/components/CommandBar/ActionBar.tsx index 24069e307cd41..183f2d6bd4c3b 100644 --- a/frontend/src/lib/components/CommandBar/ActionBar.tsx +++ b/frontend/src/lib/components/CommandBar/ActionBar.tsx @@ -1,7 +1,6 @@ import { useValues } from 'kea' import { actionBarLogic } from './actionBarLogic' - import { ActionInput } from './ActionInput' import { ActionResults } from './ActionResults' diff --git a/frontend/src/lib/components/CommandBar/ActionInput.tsx b/frontend/src/lib/components/CommandBar/ActionInput.tsx index 879b699ad1930..6a262811197f3 100644 --- a/frontend/src/lib/components/CommandBar/ActionInput.tsx +++ b/frontend/src/lib/components/CommandBar/ActionInput.tsx @@ -1,16 +1,17 @@ -import React from 'react' +import { LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { CommandFlow } from 'lib/components/CommandPalette/commandPaletteLogic' +import { IconChevronRight, IconEdit } from 'lib/lemon-ui/icons' +import React from 'react' -import { LemonInput } from '@posthog/lemon-ui' import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { actionBarLogic } from './actionBarLogic' -import { IconChevronRight, IconEdit } from 'lib/lemon-ui/icons' -import { CommandFlow } from 'lib/components/CommandPalette/commandPaletteLogic' type PrefixIconProps = { activeFlow: CommandFlow | null } + const PrefixIcon = ({ activeFlow }: PrefixIconProps): React.ReactElement | null => { if (activeFlow) { return ?? @@ -26,12 +27,11 @@ export const ActionInput = (): JSX.Element => { return (
    } - suffix={} - placeholder={activeFlow?.instruction ?? 'What would you like to do? Try some suggestions…'} + suffix={} + placeholder={activeFlow?.instruction ?? 'Run a command…'} autoFocus value={input} onChange={setInput} diff --git a/frontend/src/lib/components/CommandBar/ActionResult.tsx b/frontend/src/lib/components/CommandBar/ActionResult.tsx index 3ae32bd484a14..e10144edb6b4f 100644 --- a/frontend/src/lib/components/CommandBar/ActionResult.tsx +++ b/frontend/src/lib/components/CommandBar/ActionResult.tsx @@ -1,8 +1,8 @@ -import { useEffect, useRef } from 'react' import { useActions } from 'kea' +import { useEffect, useRef } from 'react' -import { actionBarLogic } from './actionBarLogic' import { CommandResultDisplayable } from '../CommandPalette/commandPaletteLogic' +import { actionBarLogic } from './actionBarLogic' type SearchResultProps = { result: CommandResultDisplayable @@ -22,9 +22,11 @@ export const ActionResult = ({ result, focused }: SearchResultProps): JSX.Elemen }, [focused]) return ( -
    +
    { onMouseEnterResult(result.index) }} @@ -42,6 +44,7 @@ export const ActionResult = ({ result, focused }: SearchResultProps): JSX.Elemen {result.display}
    + {focused &&
    Run command
    }
    ) diff --git a/frontend/src/lib/components/CommandBar/ActionResults.tsx b/frontend/src/lib/components/CommandBar/ActionResults.tsx index ed44ab499d03e..c104546e9210f 100644 --- a/frontend/src/lib/components/CommandBar/ActionResults.tsx +++ b/frontend/src/lib/components/CommandBar/ActionResults.tsx @@ -1,10 +1,9 @@ import { useValues } from 'kea' +import { getNameFromActionScope } from 'lib/components/CommandBar/utils' import { CommandResultDisplayable } from '../CommandPalette/commandPaletteLogic' - import { actionBarLogic } from './actionBarLogic' import { ActionResult } from './ActionResult' -import { getNameFromActionScope } from 'lib/components/CommandBar/utils' type ResultsGroupProps = { scope: string diff --git a/frontend/src/lib/components/CommandBar/CommandBar.stories.tsx b/frontend/src/lib/components/CommandBar/CommandBar.stories.tsx new file mode 100644 index 0000000000000..898e3bc1de0c4 --- /dev/null +++ b/frontend/src/lib/components/CommandBar/CommandBar.stories.tsx @@ -0,0 +1,289 @@ +import { Meta } from '@storybook/react' +import { useActions } from 'kea' +import { commandBarLogic } from 'lib/components/CommandBar/commandBarLogic' +import { BarStatus } from 'lib/components/CommandBar/types' +import { useEffect } from 'react' + +import { mswDecorator } from '~/mocks/browser' + +import { CommandBar } from './CommandBar' + +const SEARCH_RESULT = { + results: [ + { + type: 'insight', + result_id: '3b7NrJXF', + extra_fields: { + name: '', + description: '', + derived_name: 'SQL query', + }, + }, + { + type: 'insight', + result_id: 'U2W7bAq1', + extra_fields: { + name: '', + description: '', + derived_name: 'All events → All events user conversion rate', + }, + }, + { + type: 'feature_flag', + result_id: '120', + extra_fields: { + key: 'person-on-events-enabled', + name: 'person-on-events-enabled', + }, + }, + { + type: 'insight', + result_id: '44fpCyF7', + extra_fields: { + name: '', + description: '', + derived_name: 'User lifecycle based on Pageview', + }, + }, + { + type: 'feature_flag', + result_id: '150', + extra_fields: { + key: 'cs-dashboards', + name: 'cs-dashboards', + }, + }, + { + type: 'notebook', + result_id: 'b1ZyFO6K', + extra_fields: { + title: 'Notes 27/09', + text_content: 'Notes 27/09\nasd\nas\nda\ns\nd\nlalala', + }, + }, + { + type: 'insight', + result_id: 'Ap5YYl2H', + extra_fields: { + name: '', + description: '', + derived_name: + 'Pageview count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count', + }, + }, + { + type: 'insight', + result_id: '4Xaltnro', + extra_fields: { + name: '', + description: '', + derived_name: 'User paths based on page views and custom events', + }, + }, + { + type: 'insight', + result_id: 'HUkkq7Au', + extra_fields: { + name: '', + description: '', + derived_name: + 'Pageview count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count & All events count', + }, + }, + { + type: 'insight', + result_id: 'hF5z02Iw', + extra_fields: { + name: '', + description: '', + derived_name: 'Pageview count & All events count', + }, + }, + { + type: 'feature_flag', + result_id: '143', + extra_fields: { + key: 'high-frequency-batch-exports', + name: 'high-frequency-batch-exports', + }, + }, + { + type: 'feature_flag', + result_id: '126', + extra_fields: { + key: 'onboarding-v2-demo', + name: 'onboarding-v2-demo', + }, + }, + { + type: 'feature_flag', + result_id: '142', + extra_fields: { + key: 'web-analytics', + name: 'web-analytics', + }, + }, + { + type: 'insight', + result_id: '94r9bOyB', + extra_fields: { + name: '', + description: '', + derived_name: 'Pageview count & All events count', + }, + }, + { + type: 'dashboard', + result_id: '1', + extra_fields: { + name: '🔑 Key metrics', + description: 'Company overview.', + }, + }, + { + type: 'notebook', + result_id: 'eq4n8PQY', + extra_fields: { + title: 'asd', + text_content: 'asd', + }, + }, + { + type: 'insight', + result_id: 'QcCPEk7d', + extra_fields: { + name: 'Daily unique visitors over time', + description: null, + derived_name: '$pageview unique users & All events count', + }, + }, + { + type: 'feature_flag', + result_id: '133', + extra_fields: { + key: 'feedback-scene', + name: 'feedback-scene', + }, + }, + { + type: 'insight', + result_id: 'PWwez0ma', + extra_fields: { + name: 'Most popular pages', + description: null, + derived_name: null, + }, + }, + { + type: 'insight', + result_id: 'HKTERZ40', + extra_fields: { + name: 'Feature Flag calls made by unique users per variant', + description: + 'Shows the number of unique user calls made on feature flag per variant with key: notebooks', + derived_name: null, + }, + }, + { + type: 'feature_flag', + result_id: '161', + extra_fields: { + key: 'console-recording-search', + name: 'console-recording-search', + }, + }, + { + type: 'feature_flag', + result_id: '134', + extra_fields: { + key: 'early-access-feature', + name: 'early-access-feature', + }, + }, + { + type: 'insight', + result_id: 'uE7xieYc', + extra_fields: { + name: '', + description: '', + derived_name: 'Pageview count', + }, + }, + { + type: 'feature_flag', + result_id: '159', + extra_fields: { + key: 'surveys-multiple-questions', + name: 'surveys-multiple-questions', + }, + }, + { + type: 'insight', + result_id: 'AVPsaax4', + extra_fields: { + name: 'Monthly app revenue', + description: null, + derived_name: null, + }, + }, + ], + counts: { + insight: 80, + dashboard: 14, + experiment: 1, + feature_flag: 66, + notebook: 2, + action: 4, + cohort: 3, + }, +} + +const meta: Meta = { + title: 'Components/Command Bar', + component: CommandBar, + decorators: [ + mswDecorator({ + get: { + '/api/projects/:team_id/search': SEARCH_RESULT, + }, + }), + ], + parameters: { + layout: 'fullscreen', + testOptions: { + snapshotTargetSelector: '[data-attr="command-bar"]', + }, + viewMode: 'story', + }, +} +export default meta + +export function Search(): JSX.Element { + const { setCommandBar } = useActions(commandBarLogic) + + useEffect(() => { + setCommandBar(BarStatus.SHOW_SEARCH) + }, []) + + return +} + +export function Actions(): JSX.Element { + const { setCommandBar } = useActions(commandBarLogic) + + useEffect(() => { + setCommandBar(BarStatus.SHOW_ACTIONS) + }, []) + + return +} + +export function Shortcuts(): JSX.Element { + const { setCommandBar } = useActions(commandBarLogic) + + useEffect(() => { + setCommandBar(BarStatus.SHOW_SHORTCUTS) + }, []) + + return +} diff --git a/frontend/src/lib/components/CommandBar/CommandBar.tsx b/frontend/src/lib/components/CommandBar/CommandBar.tsx index cd906715a4cdc..480cf294d9e3a 100644 --- a/frontend/src/lib/components/CommandBar/CommandBar.tsx +++ b/frontend/src/lib/components/CommandBar/CommandBar.tsx @@ -1,29 +1,47 @@ -import { useRef } from 'react' -import { useActions, useValues } from 'kea' +import './index.scss' +import { useActions, useValues } from 'kea' import { useOutsideClickHandler } from 'lib/hooks/useOutsideClickHandler' +import { forwardRef, useRef } from 'react' +import { ActionBar } from './ActionBar' import { commandBarLogic } from './commandBarLogic' +import { SearchBar } from './SearchBar' +import { Shortcuts } from './Shortcuts' import { BarStatus } from './types' -import './index.scss' -import { SearchBar } from './SearchBar' -import { ActionBar } from './ActionBar' +interface CommandBarOverlayProps { + barStatus: BarStatus + children?: React.ReactNode +} -const CommandBarOverlay = ({ children }: { children?: React.ReactNode }): JSX.Element => ( -
    - {children} -
    -) +const CommandBarOverlay = forwardRef(function CommandBarOverlayInternal( + { barStatus, children }, + ref +): JSX.Element { + return ( +
    +
    + {children} +
    +
    + ) +}) export function CommandBar(): JSX.Element | null { const containerRef = useRef(null) @@ -37,15 +55,10 @@ export function CommandBar(): JSX.Element | null { } return ( - -
    - {barStatus === BarStatus.SHOW_SEARCH ? : } -
    + + {barStatus === BarStatus.SHOW_SEARCH && } + {barStatus === BarStatus.SHOW_ACTIONS && } + {barStatus === BarStatus.SHOW_SHORTCUTS && } ) } diff --git a/frontend/src/lib/components/CommandBar/SearchBar.tsx b/frontend/src/lib/components/CommandBar/SearchBar.tsx index 7a4163e487a91..a7d1d6898913a 100644 --- a/frontend/src/lib/components/CommandBar/SearchBar.tsx +++ b/frontend/src/lib/components/CommandBar/SearchBar.tsx @@ -2,7 +2,6 @@ import { useMountedLogic } from 'kea' import { useRef } from 'react' import { searchBarLogic } from './searchBarLogic' - import { SearchInput } from './SearchInput' import { SearchResults } from './SearchResults' import { SearchTabs } from './SearchTabs' diff --git a/frontend/src/lib/components/CommandBar/SearchBarTab.tsx b/frontend/src/lib/components/CommandBar/SearchBarTab.tsx index c2dcb75f0917b..e71cda427e5bd 100644 --- a/frontend/src/lib/components/CommandBar/SearchBarTab.tsx +++ b/frontend/src/lib/components/CommandBar/SearchBarTab.tsx @@ -1,10 +1,10 @@ -import { RefObject } from 'react' import { useActions, useValues } from 'kea' +import { Spinner } from 'lib/lemon-ui/Spinner' +import { RefObject } from 'react' import { resultTypeToName } from './constants' import { searchBarLogic } from './searchBarLogic' import { ResultTypeWithAll } from './types' -import { Spinner } from 'lib/lemon-ui/Spinner' type SearchBarTabProps = { type: ResultTypeWithAll @@ -17,7 +17,9 @@ export const SearchBarTab = ({ type, active, count, inputRef }: SearchBarTabProp const { setActiveTab } = useActions(searchBarLogic) return (
    { setActiveTab(type) inputRef.current?.focus() diff --git a/frontend/src/lib/components/CommandBar/SearchInput.tsx b/frontend/src/lib/components/CommandBar/SearchInput.tsx index ce03cc5af4cc7..3d79b64531e78 100644 --- a/frontend/src/lib/components/CommandBar/SearchInput.tsx +++ b/frontend/src/lib/components/CommandBar/SearchInput.tsx @@ -1,30 +1,35 @@ +import { LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { isMac } from 'lib/utils' +import { forwardRef, Ref } from 'react' +import { teamLogic } from 'scenes/teamLogic' -import { LemonInput } from '@posthog/lemon-ui' import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { searchBarLogic } from './searchBarLogic' -import { forwardRef, Ref } from 'react' -import { teamLogic } from 'scenes/teamLogic' export const SearchInput = forwardRef(function _SearchInput(_, ref: Ref): JSX.Element { const { currentTeam } = useValues(teamLogic) const { searchQuery } = useValues(searchBarLogic) const { setSearchQuery } = useActions(searchBarLogic) + const modifierKey = isMac() ? '⌘' : '^' + const placeholder = currentTeam + ? `Search the ${currentTeam.name} project or press ${modifierKey}⇧K to go to commands…` + : `Search or press ${modifierKey}⇧K to go to commands…` + return (
    } + suffix={} + placeholder={placeholder} autoFocus value={searchQuery} onChange={setSearchQuery} - placeholder={currentTeam ? `Search the ${currentTeam.name} project…` : 'Search…'} />
    ) diff --git a/frontend/src/lib/components/CommandBar/SearchResult.tsx b/frontend/src/lib/components/CommandBar/SearchResult.tsx index 8c5364ca87101..9a14a6203fa5a 100644 --- a/frontend/src/lib/components/CommandBar/SearchResult.tsx +++ b/frontend/src/lib/components/CommandBar/SearchResult.tsx @@ -1,10 +1,17 @@ -import { useLayoutEffect, useRef } from 'react' +import { LemonSkeleton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { useLayoutEffect, useRef } from 'react' +import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { mathsLogic } from 'scenes/trends/mathsLogic' + +import { cohortsModel } from '~/models/cohortsModel' +import { groupsModel } from '~/models/groupsModel' +import { Node } from '~/queries/schema' +import { FilterType } from '~/types' import { resultTypeToName } from './constants' import { searchBarLogic, urlForResult } from './searchBarLogic' import { SearchResult as SearchResultType } from './types' -import { LemonSkeleton } from '@posthog/lemon-ui' type SearchResultProps = { result: SearchResultType @@ -41,9 +48,7 @@ export const SearchResult = ({ result, resultIndex, focused, keyboardFocused }: return (
    { if (isAutoScrolling) { return @@ -88,9 +93,23 @@ type ResultNameProps = { } export const ResultName = ({ result }: ResultNameProps): JSX.Element | null => { + const { aggregationLabel } = useValues(groupsModel) + const { cohortsById } = useValues(cohortsModel) + const { mathDefinitions } = useValues(mathsLogic) + const { type, extra_fields } = result if (type === 'insight') { - return extra_fields.name ? {extra_fields.name} : {extra_fields.derived_name} + return extra_fields.name ? ( + {extra_fields.name} + ) : ( + + {summarizeInsight(extra_fields.query as Node | null, extra_fields.filters as Partial, { + aggregationLabel, + cohortsById, + mathDefinitions, + })} + + ) } else if (type === 'feature_flag') { return {extra_fields.key} } else if (type === 'notebook') { diff --git a/frontend/src/lib/components/CommandBar/SearchResultPreview.tsx b/frontend/src/lib/components/CommandBar/SearchResultPreview.tsx index f91b09a865dd3..57bd498e353ea 100644 --- a/frontend/src/lib/components/CommandBar/SearchResultPreview.tsx +++ b/frontend/src/lib/components/CommandBar/SearchResultPreview.tsx @@ -1,10 +1,9 @@ import { useValues } from 'kea' +import { ResultDescription, ResultName } from 'lib/components/CommandBar/SearchResult' import { resultTypeToName } from './constants' import { searchBarLogic } from './searchBarLogic' -import { ResultDescription, ResultName } from 'lib/components/CommandBar/SearchResult' - export const SearchResultPreview = (): JSX.Element | null => { const { activeResultIndex, filterSearchResults } = useValues(searchBarLogic) diff --git a/frontend/src/lib/components/CommandBar/SearchResults.tsx b/frontend/src/lib/components/CommandBar/SearchResults.tsx index 9a1e217d69b4d..c385bd315ad44 100644 --- a/frontend/src/lib/components/CommandBar/SearchResults.tsx +++ b/frontend/src/lib/components/CommandBar/SearchResults.tsx @@ -1,7 +1,6 @@ import { useValues } from 'kea' import { DetectiveHog } from '../hedgehogs' - import { searchBarLogic } from './searchBarLogic' import { SearchResult, SearchResultSkeleton } from './SearchResult' import { SearchResultPreview } from './SearchResultPreview' diff --git a/frontend/src/lib/components/CommandBar/SearchTabs.tsx b/frontend/src/lib/components/CommandBar/SearchTabs.tsx index fe6e9a9edb2ad..4e3d65c29cadd 100644 --- a/frontend/src/lib/components/CommandBar/SearchTabs.tsx +++ b/frontend/src/lib/components/CommandBar/SearchTabs.tsx @@ -17,7 +17,7 @@ export const SearchTabs = ({ inputRef }: SearchTabsProps): JSX.Element | null => } return ( -
    +
    {Object.entries(searchResponse.counts).map(([type, count]) => ( { + useMountedLogic(shortcutsLogic) + + return ( +
    +

    Keyboard shortcuts

    +

    Site-wide shortcuts

    +
    +
    + Open search +
    +
    + Open command palette +
    +
    +
    + ) +} diff --git a/frontend/src/lib/components/CommandBar/actionBarLogic.ts b/frontend/src/lib/components/CommandBar/actionBarLogic.ts index aaee36bc40e3b..c936929fed9c2 100644 --- a/frontend/src/lib/components/CommandBar/actionBarLogic.ts +++ b/frontend/src/lib/components/CommandBar/actionBarLogic.ts @@ -1,12 +1,10 @@ -import { kea, path, listeners, connect, afterMount, beforeUnmount } from 'kea' +import { afterMount, beforeUnmount, connect, kea, listeners, path } from 'kea' import { commandPaletteLogic } from '../CommandPalette/commandPaletteLogic' +import type { actionBarLogicType } from './actionBarLogicType' import { commandBarLogic } from './commandBarLogic' - import { BarStatus } from './types' -import type { actionBarLogicType } from './actionBarLogicType' - export const actionBarLogic = kea([ path(['lib', 'components', 'CommandBar', 'actionBarLogic']), connect({ @@ -65,7 +63,7 @@ export const actionBarLogic = kea([ // navigate to previous result event.preventDefault() actions.onArrowUp() - } else if (event.key === 'Escape') { + } else if (event.key === 'Escape' && event.repeat === false) { event.preventDefault() if (values.activeFlow) { @@ -79,7 +77,7 @@ export const actionBarLogic = kea([ actions.hidePalette() } } else if (event.key === 'Backspace') { - if (values.input.length === 0) { + if (values.input.length === 0 && event.repeat === false) { // transition to search when pressing backspace with empty input actions.setCommandBar(BarStatus.SHOW_SEARCH) } diff --git a/frontend/src/lib/components/CommandBar/commandBarLogic.ts b/frontend/src/lib/components/CommandBar/commandBarLogic.ts index 59aeab9a38862..ef6df079ddea1 100644 --- a/frontend/src/lib/components/CommandBar/commandBarLogic.ts +++ b/frontend/src/lib/components/CommandBar/commandBarLogic.ts @@ -1,7 +1,7 @@ -import { kea, path, actions, reducers, afterMount, beforeUnmount } from 'kea' -import { BarStatus } from './types' +import { actions, afterMount, beforeUnmount, kea, path, reducers } from 'kea' import type { commandBarLogicType } from './commandBarLogicType' +import { BarStatus } from './types' export const commandBarLogic = kea([ path(['lib', 'components', 'CommandBar', 'commandBarLogic']), @@ -10,6 +10,7 @@ export const commandBarLogic = kea([ hideCommandBar: true, toggleSearchBar: true, toggleActionsBar: true, + toggleShortcutOverview: true, }), reducers({ barStatus: [ @@ -18,9 +19,15 @@ export const commandBarLogic = kea([ setCommandBar: (_, { status }) => status, hideCommandBar: () => BarStatus.HIDDEN, toggleSearchBar: (previousState) => - previousState === BarStatus.HIDDEN ? BarStatus.SHOW_SEARCH : BarStatus.HIDDEN, + [BarStatus.HIDDEN, BarStatus.SHOW_SHORTCUTS].includes(previousState) + ? BarStatus.SHOW_SEARCH + : BarStatus.HIDDEN, toggleActionsBar: (previousState) => - previousState === BarStatus.HIDDEN ? BarStatus.SHOW_ACTIONS : BarStatus.HIDDEN, + [BarStatus.HIDDEN, BarStatus.SHOW_SHORTCUTS].includes(previousState) + ? BarStatus.SHOW_ACTIONS + : BarStatus.HIDDEN, + toggleShortcutOverview: (previousState) => + previousState === BarStatus.HIDDEN ? BarStatus.SHOW_SHORTCUTS : previousState, }, ], }), @@ -36,6 +43,8 @@ export const commandBarLogic = kea([ // cmd+k opens search actions.toggleSearchBar() } + } else if (event.shiftKey && event.key === '?') { + actions.toggleShortcutOverview() } } window.addEventListener('keydown', cache.onKeyDown) diff --git a/frontend/src/lib/components/CommandBar/index.scss b/frontend/src/lib/components/CommandBar/index.scss index 80621cf83d7c9..c8a200a7f5740 100644 --- a/frontend/src/lib/components/CommandBar/index.scss +++ b/frontend/src/lib/components/CommandBar/index.scss @@ -1,4 +1,17 @@ .CommandBar__input { border-color: transparent !important; border-radius: 0; + height: 2.75rem; + padding-left: 0.75rem; + padding-right: 0.375rem; +} + +.SearchBarTab { + &:hover { + border-top: 2px solid var(--border-3000); + } + + &.SearchBarTab__active { + border-color: var(--primary-3000); + } } diff --git a/frontend/src/lib/components/CommandBar/searchBarLogic.ts b/frontend/src/lib/components/CommandBar/searchBarLogic.ts index 91c649fc9eb89..2f3b04715c598 100644 --- a/frontend/src/lib/components/CommandBar/searchBarLogic.ts +++ b/frontend/src/lib/components/CommandBar/searchBarLogic.ts @@ -1,12 +1,12 @@ -import { kea, path, actions, reducers, selectors, listeners, connect, afterMount, beforeUnmount } from 'kea' +import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import { router } from 'kea-router' - import api from 'lib/api' import { urls } from 'scenes/urls' + import { InsightShortId } from '~/types' -import { commandBarLogic } from './commandBarLogic' +import { commandBarLogic } from './commandBarLogic' import type { searchBarLogicType } from './searchBarLogicType' import { BarStatus, ResultTypeWithAll, SearchResponse, SearchResult } from './types' @@ -90,6 +90,10 @@ export const searchBarLogic = kea([ (s) => [s.keyboardResultIndex, s.hoverResultIndex], (keyboardResultIndex: number, hoverResultIndex: number | null) => hoverResultIndex || keyboardResultIndex, ], + tabs: [ + (s) => [s.searchCounts], + (counts): ResultTypeWithAll[] => ['all', ...(Object.keys(counts || {}) as ResultTypeWithAll[])], + ], }), listeners(({ values, actions }) => ({ openResult: ({ index }) => { @@ -118,7 +122,7 @@ export const searchBarLogic = kea([ // navigate to previous result event.preventDefault() actions.onArrowUp(values.activeResultIndex, values.maxIndex) - } else if (event.key === 'Escape') { + } else if (event.key === 'Escape' && event.repeat === false) { // hide command bar actions.hideCommandBar() } else if (event.key === '>') { @@ -133,6 +137,16 @@ export const searchBarLogic = kea([ event.preventDefault() actions.setCommandBar(BarStatus.SHOW_ACTIONS) } + } else if (event.key === 'Tab') { + event.preventDefault() + const currentIndex = values.tabs.findIndex((tab) => tab === values.activeTab) + if (event.shiftKey) { + const prevIndex = currentIndex === 0 ? values.tabs.length - 1 : currentIndex - 1 + actions.setActiveTab(values.tabs[prevIndex]) + } else { + const nextIndex = currentIndex === values.tabs.length - 1 ? 0 : currentIndex + 1 + actions.setActiveTab(values.tabs[nextIndex]) + } } } window.addEventListener('keydown', cache.onKeyDown) diff --git a/frontend/src/lib/components/CommandBar/shortcutsLogic.ts b/frontend/src/lib/components/CommandBar/shortcutsLogic.ts new file mode 100644 index 0000000000000..4e70c5920f41c --- /dev/null +++ b/frontend/src/lib/components/CommandBar/shortcutsLogic.ts @@ -0,0 +1,25 @@ +import { afterMount, beforeUnmount, connect, kea, path } from 'kea' + +import { commandBarLogic } from './commandBarLogic' +import type { shortcutsLogicType } from './shortcutsLogicType' + +export const shortcutsLogic = kea([ + path(['lib', 'components', 'CommandBar', 'shortcutsLogic']), + connect({ + actions: [commandBarLogic, ['hideCommandBar']], + }), + afterMount(({ actions, cache }) => { + // register keyboard shortcuts + cache.onKeyDown = (event: KeyboardEvent) => { + if (event.key === 'Escape') { + // hide command bar + actions.hideCommandBar() + } + } + window.addEventListener('keydown', cache.onKeyDown) + }), + beforeUnmount(({ cache }) => { + // unregister keyboard shortcuts + window.removeEventListener('keydown', cache.onKeyDown) + }), +]) diff --git a/frontend/src/lib/components/CommandBar/types.ts b/frontend/src/lib/components/CommandBar/types.ts index 1f3278f3727f6..3a6a482c26453 100644 --- a/frontend/src/lib/components/CommandBar/types.ts +++ b/frontend/src/lib/components/CommandBar/types.ts @@ -2,6 +2,7 @@ export enum BarStatus { HIDDEN = 'hidden', SHOW_SEARCH = 'show_search', SHOW_ACTIONS = 'show_actions', + SHOW_SHORTCUTS = 'show_shortcuts', } export type ResultType = 'action' | 'cohort' | 'insight' | 'dashboard' | 'experiment' | 'feature_flag' | 'notebook' diff --git a/frontend/src/lib/components/CommandPalette/CommandInput.tsx b/frontend/src/lib/components/CommandPalette/CommandInput.tsx index 38186438f41ac..57a6821ae27db 100644 --- a/frontend/src/lib/components/CommandPalette/CommandInput.tsx +++ b/frontend/src/lib/components/CommandPalette/CommandInput.tsx @@ -1,7 +1,8 @@ -import { useValues, useActions } from 'kea' -import { commandPaletteLogic } from './commandPaletteLogic' +import { useActions, useValues } from 'kea' import { IconEdit, IconExclamation, IconMagnifier } from 'lib/lemon-ui/icons' +import { commandPaletteLogic } from './commandPaletteLogic' + export function CommandInput(): JSX.Element { const { input, isSqueak, activeFlow } = useValues(commandPaletteLogic) const { setInput } = useActions(commandPaletteLogic) diff --git a/frontend/src/lib/components/CommandPalette/CommandPalette.scss b/frontend/src/lib/components/CommandPalette/CommandPalette.scss index e2622169149a0..55079ad3ac496 100644 --- a/frontend/src/lib/components/CommandPalette/CommandPalette.scss +++ b/frontend/src/lib/components/CommandPalette/CommandPalette.scss @@ -121,5 +121,5 @@ .palette__icon { display: flex; align-items: center; - font-size: 1rem; + font-size: 1.25rem; } diff --git a/frontend/src/lib/components/CommandPalette/CommandPalette.tsx b/frontend/src/lib/components/CommandPalette/CommandPalette.tsx index e32c98f3175de..20f801a408170 100644 --- a/frontend/src/lib/components/CommandPalette/CommandPalette.tsx +++ b/frontend/src/lib/components/CommandPalette/CommandPalette.tsx @@ -1,15 +1,17 @@ -import { useRef, useMemo } from 'react' -import { useOutsideClickHandler } from 'lib/hooks/useOutsideClickHandler' -import { useMountedLogic, useValues, useActions } from 'kea' -import { commandPaletteLogic } from './commandPaletteLogic' -import { CommandInput } from './CommandInput' -import { CommandResults } from './CommandResults' -import { useEventListener } from 'lib/hooks/useEventListener' -import squeakFile from 'public/squeak.mp3' import './CommandPalette.scss' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' + +import { useActions, useMountedLogic, useValues } from 'kea' import { FEATURE_FLAGS } from 'lib/constants' +import { useEventListener } from 'lib/hooks/useEventListener' +import { useOutsideClickHandler } from 'lib/hooks/useOutsideClickHandler' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import squeakFile from 'public/squeak.mp3' +import { useMemo, useRef } from 'react' + import { CommandBar } from '../CommandBar/CommandBar' +import { CommandInput } from './CommandInput' +import { commandPaletteLogic } from './commandPaletteLogic' +import { CommandResults } from './CommandResults' /** Use the new Cmd+K search when the respective feature flag is enabled. */ export function CommandPalette(): JSX.Element { diff --git a/frontend/src/lib/components/CommandPalette/CommandResults.tsx b/frontend/src/lib/components/CommandPalette/CommandResults.tsx index 81c7f2d087a0e..37d1814dcbc11 100644 --- a/frontend/src/lib/components/CommandPalette/CommandResults.tsx +++ b/frontend/src/lib/components/CommandPalette/CommandResults.tsx @@ -1,7 +1,8 @@ +import { useActions, useMountedLogic, useValues } from 'kea' +import { useEventListener } from 'lib/hooks/useEventListener' import { useEffect, useRef } from 'react' + import { CommandResultDisplayable } from './commandPaletteLogic' -import { useEventListener } from 'lib/hooks/useEventListener' -import { useActions, useMountedLogic, useValues } from 'kea' import { commandPaletteLogic } from './commandPaletteLogic' interface CommandResultProps { diff --git a/frontend/src/lib/components/CommandPalette/DebugCHQueries.tsx b/frontend/src/lib/components/CommandPalette/DebugCHQueries.tsx index b6590a40ed8eb..2a87d26d1deea 100644 --- a/frontend/src/lib/components/CommandPalette/DebugCHQueries.tsx +++ b/frontend/src/lib/components/CommandPalette/DebugCHQueries.tsx @@ -1,14 +1,15 @@ +import { actions, afterMount, kea, path, reducers, selectors, useActions, useValues } from 'kea' +import { loaders } from 'kea-loaders' import api from 'lib/api' import { dayjs } from 'lib/dayjs' +import { IconRefresh } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { LemonTable } from 'lib/lemon-ui/LemonTable' + import { CodeSnippet, Language } from '../CodeSnippet' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { actions, afterMount, kea, reducers, selectors, useActions, useValues, path } from 'kea' -import { loaders } from 'kea-loaders' import type { debugCHQueriesLogicType } from './DebugCHQueriesType' -import { IconRefresh } from 'lib/lemon-ui/icons' export function openCHQueriesDebugModal(): void { LemonDialog.open({ diff --git a/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx b/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx index 27057c4de09eb..8dedc08691066 100644 --- a/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx +++ b/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx @@ -1,54 +1,70 @@ -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import { router } from 'kea-router' -import type { commandPaletteLogicType } from './commandPaletteLogicType' -import Fuse from 'fuse.js' -import { dashboardsModel } from '~/models/dashboardsModel' -import { Parser } from 'expr-eval' -import { DashboardType, InsightType } from '~/types' -import api from 'lib/api' -import { isMobile, isURL, sample, uniqueBy } from 'lib/utils' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { userLogic } from 'scenes/userLogic' -import { personalAPIKeysLogic } from '../../../scenes/settings/user/personalAPIKeysLogic' -import { teamLogic } from 'scenes/teamLogic' -import posthog from 'posthog-js' -import { openCHQueriesDebugModal } from './DebugCHQueries' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { urls } from 'scenes/urls' -import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' import { - IconAction, IconApps, - IconBarChart, - IconCalculate, - IconCheckmark, - IconCohort, - IconComment, - IconCorporate, - IconCottage, - IconEmojiPeople, - IconFlag, - IconFunnelHorizontal, - IconGauge, + IconAsterisk, + IconCalculator, + IconChat, + IconCheck, + IconCursor, + IconDashboard, + IconDatabase, + IconDay, + IconExternal, + IconFunnels, + IconGear, IconGithub, + IconGraph, + IconHogQL, + IconHome, IconKeyboard, + IconLeave, + IconLifecycle, + IconList, IconLive, - IconLockOpen, - IconLogout, - IconOpenInNew, - IconPerson, - IconPersonFilled, - IconRecording, + IconNight, + IconNotebook, + IconPageChart, + IconPeople, + IconPeopleFilled, + IconPieChart, + IconRetention, + IconRewindPlay, + IconRocket, IconServer, - IconSettings, - IconTableChart, - IconTools, - IconTrendingFlat, - IconTrendingUp, -} from 'lib/lemon-ui/icons' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' + IconStickiness, + IconTestTube, + IconThoughtBubble, + IconToggle, + IconToolbar, + IconTrends, + IconUnlock, + IconUserPaths, +} from '@posthog/icons' +import { Parser } from 'expr-eval' +import Fuse from 'fuse.js' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' +import { router } from 'kea-router' +import api from 'lib/api' import { FEATURE_FLAGS } from 'lib/constants' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { isMobile, isURL, uniqueBy } from 'lib/utils' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import posthog from 'posthog-js' +import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' +import { insightTypeURL } from 'scenes/insights/utils' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { ThemeIcon } from '~/layout/navigation-3000/components/Navbar' +import { themeLogic } from '~/layout/navigation-3000/themeLogic' +import { dashboardsModel } from '~/models/dashboardsModel' +import { DashboardType, InsightType } from '~/types' + +import { personalAPIKeysLogic } from '../../../scenes/settings/user/personalAPIKeysLogic' +import type { commandPaletteLogicType } from './commandPaletteLogicType' +import { openCHQueriesDebugModal } from './DebugCHQueries' // If CommandExecutor returns CommandFlow, flow will be entered export type CommandExecutor = () => CommandFlow | void @@ -118,7 +134,7 @@ function resolveCommand(source: Command | CommandFlow, argument?: string, prefix export const commandPaletteLogic = kea([ path(['lib', 'components', 'CommandPalette', 'commandPaletteLogic']), connect({ - actions: [personalAPIKeysLogic, ['createKey'], router, ['push']], + actions: [personalAPIKeysLogic, ['createKey'], router, ['push'], themeLogic, ['overrideTheme']], values: [teamLogic, ['currentTeam'], userLogic, ['user'], featureFlagLogic, ['featureFlags']], logic: [preflightLogic], }), @@ -240,7 +256,7 @@ export const commandPaletteLogic = kea([ key: 'custom_dashboards', resolver: dashboards.map((dashboard: DashboardType) => ({ key: `dashboard_${dashboard.id}`, - icon: IconTableChart, + icon: IconPageChart, display: `Go to dashboard: ${dashboard.name}`, executor: () => { const { push } = router.actions @@ -317,7 +333,7 @@ export const commandPaletteLogic = kea([ .search(argument) .slice(0, RESULTS_MAX) .map((result) => result.item) - : sample(fusableResults, RESULTS_MAX - guaranteedResults.length) + : fusableResults.slice(0, RESULTS_MAX) return guaranteedResults.concat(fusedResults) }, ], @@ -395,7 +411,7 @@ export const commandPaletteLogic = kea([ key: `person-${person.distinct_ids[0]}`, resolver: [ { - icon: IconPersonFilled, + icon: IconPeopleFilled, display: `View person ${input}`, executor: () => { const { push } = router.actions @@ -419,67 +435,128 @@ export const commandPaletteLogic = kea([ prefixes: ['open', 'visit'], resolver: [ { - icon: IconGauge, + icon: IconDashboard, display: 'Go to Dashboards', executor: () => { push(urls.dashboards()) }, }, { - icon: IconBarChart, + icon: IconHome, + display: 'Go to Project homepage', + executor: () => { + push(urls.projectHomepage()) + }, + }, + { + icon: IconGraph, display: 'Go to Insights', executor: () => { push(urls.savedInsights()) }, }, { - icon: IconTrendingUp, - display: 'Go to Trends', + icon: IconTrends, + display: 'Create a new Trend insight', executor: () => { // TODO: Don't reset insight on change push(urls.insightNew({ insight: InsightType.TRENDS })) }, }, { - icon: IconFunnelHorizontal, - display: 'Go to Funnels', + icon: IconFunnels, + display: 'Create a new Funnel insight', executor: () => { // TODO: Don't reset insight on change push(urls.insightNew({ insight: InsightType.FUNNELS })) }, }, { - icon: IconTrendingFlat, - display: 'Go to Retention', + icon: IconRetention, + display: 'Create a new Retention insight', executor: () => { // TODO: Don't reset insight on change push(urls.insightNew({ insight: InsightType.RETENTION })) }, }, { - icon: IconEmojiPeople, - display: 'Go to Paths', + icon: IconUserPaths, + display: 'Create a new Paths insight', executor: () => { // TODO: Don't reset insight on change push(urls.insightNew({ insight: InsightType.PATHS })) }, }, + { + icon: IconStickiness, + display: 'Create a new Stickiness insight', + executor: () => { + // TODO: Don't reset insight on change + push(urls.insightNew({ insight: InsightType.STICKINESS })) + }, + }, + { + icon: IconLifecycle, + display: 'Create a new Lifecycle insight', + executor: () => { + // TODO: Don't reset insight on change + push(urls.insightNew({ insight: InsightType.LIFECYCLE })) + }, + }, + { + icon: IconHogQL, + display: 'Create a new HogQL insight', + synonyms: ['hogql', 'sql'], + executor: () => { + // TODO: Don't reset insight on change + push(insightTypeURL[InsightType.SQL]) + }, + }, + { + icon: IconNotebook, + display: 'Go to Notebooks', + executor: () => { + push(urls.notebooks()) + }, + }, { icon: IconLive, - display: 'Go to Events', + display: 'Go to Events explorer', executor: () => { push(urls.events()) }, }, { - icon: IconAction, + icon: IconDatabase, + display: 'Go to Data management', + synonyms: ['events'], + executor: () => { + push(urls.eventDefinitions()) + }, + }, + { + icon: IconCursor, display: 'Go to Actions', executor: () => { push(urls.actions()) }, }, { - icon: IconPerson, + icon: IconList, + display: 'Go to Properties', + executor: () => { + push(urls.propertyDefinitions()) + }, + }, + { + icon: IconThoughtBubble, + display: 'Go to Annotations', + executor: () => { + push(urls.annotations()) + }, + }, + { + icon: IconPeople, display: 'Go to Persons', synonyms: ['people'], executor: () => { @@ -487,77 +564,110 @@ export const commandPaletteLogic = kea([ }, }, { - icon: IconCohort, + icon: IconPeople, display: 'Go to Cohorts', executor: () => { push(urls.cohorts()) }, }, + ...(values.featureFlags[FEATURE_FLAGS.WEB_ANALYTICS] + ? [ + { + icon: IconPieChart, + display: 'Go to Web analytics', + executor: () => { + push(urls.webAnalytics()) + }, + }, + ] + : []), + ...(values.featureFlags[FEATURE_FLAGS.DATA_WAREHOUSE] + ? [ + { + icon: IconServer, + display: 'Go to Data warehouse', + executor: () => { + push(urls.dataWarehouse()) + }, + }, + ] + : []), + { + display: 'Go to Session replay', + icon: IconRewindPlay, + executor: () => { + push(urls.replay()) + }, + }, { - icon: IconFlag, - display: 'Go to Feature Flags', - synonyms: ['feature flags', 'a/b tests'], + display: 'Go to Surveys', + icon: IconChat, + executor: () => { + push(urls.surveys()) + }, + }, + { + icon: IconToggle, + display: 'Go to Feature flags', executor: () => { push(urls.featureFlags()) }, }, { - icon: IconComment, - display: 'Go to Annotations', + icon: IconTestTube, + display: 'Go to A/B testing', executor: () => { - push(urls.annotations()) + push(urls.experiments()) }, }, { - icon: IconCorporate, - display: 'Go to Team members', - synonyms: ['organization', 'members', 'invites', 'teammates'], + icon: IconRocket, + display: 'Go to Early access features', executor: () => { - push(urls.settings('organization')) + push(urls.earlyAccessFeatures()) }, }, { - icon: IconCottage, - display: 'Go to project homepage', + icon: IconApps, + display: 'Go to Apps', + synonyms: ['integrations'], executor: () => { - push(urls.projectHomepage()) + push(urls.projectApps()) }, }, { - icon: IconSettings, - display: 'Go to Project settings', + icon: IconToolbar, + display: 'Go to Toolbar', executor: () => { - push(urls.settings('project')) + push(urls.toolbarLaunch()) }, }, { - icon: () => ( - - ), - display: 'Go to My settings', - synonyms: ['account'], + icon: IconGear, + display: 'Go to Project settings', executor: () => { - push(urls.settings('user')) + push(urls.settings('project')) }, }, { - icon: IconApps, - display: 'Go to Apps', - synonyms: ['integrations'], + icon: IconGear, + display: 'Go to Organization settings', executor: () => { - push(urls.projectApps()) + push(urls.settings('organization')) }, }, { - icon: IconServer, - display: 'Go to Instance status & settings', - synonyms: ['redis', 'celery', 'django', 'postgres', 'backend', 'service', 'online'], + icon: () => ( + + ), + display: 'Go to User settings', + synonyms: ['account', 'profile'], executor: () => { - push(urls.instanceStatus()) + push(urls.settings('user')) }, }, { - icon: IconLogout, + icon: IconLeave, display: 'Log out', executor: () => { userLogic.actions.logout() @@ -575,7 +685,7 @@ export const commandPaletteLogic = kea([ preflightLogic.values.preflight?.is_debug || preflightLogic.values.preflight?.instance_preferences?.debug_queries ? { - icon: IconTools, + icon: IconDatabase, display: 'Debug ClickHouse Queries', executor: () => openCHQueriesDebugModal(), } @@ -586,7 +696,7 @@ export const commandPaletteLogic = kea([ key: 'debug-copy-session-recording-url', scope: GLOBAL_COMMAND_SCOPE, resolver: { - icon: IconRecording, + icon: IconRewindPlay, display: 'Debug: Copy the session recording link to clipboard', executor: () => { const url = posthog.get_session_replay_url({ withTimestamp: true, timestampLookBack: 30 }) @@ -608,7 +718,7 @@ export const commandPaletteLogic = kea([ return isNaN(result) ? null : { - icon: IconCalculate, + icon: IconCalculator, display: `= ${result}`, guarantee: true, executor: () => { @@ -628,7 +738,7 @@ export const commandPaletteLogic = kea([ resolver: (argument) => { const results: CommandResultTemplate[] = (teamLogic.values.currentTeam?.app_urls ?? []).map( (url: string) => ({ - icon: IconOpenInNew, + icon: IconExternal, display: `Open ${url}`, synonyms: [`Visit ${url}`], executor: () => { @@ -638,7 +748,7 @@ export const commandPaletteLogic = kea([ ) if (argument && isURL(argument)) { results.push({ - icon: IconOpenInNew, + icon: IconExternal, display: `Open ${argument}`, synonyms: [`Visit ${argument}`], executor: () => { @@ -647,7 +757,7 @@ export const commandPaletteLogic = kea([ }) } results.push({ - icon: IconOpenInNew, + icon: IconExternal, display: 'Open PostHog Docs', synonyms: ['technical documentation'], executor: () => { @@ -662,7 +772,7 @@ export const commandPaletteLogic = kea([ key: 'create-personal-api-key', scope: GLOBAL_COMMAND_SCOPE, resolver: { - icon: IconLockOpen, + icon: IconUnlock, display: 'Create Personal API Key', executor: () => ({ instruction: 'Give your key a label', @@ -671,7 +781,7 @@ export const commandPaletteLogic = kea([ resolver: (argument) => { if (argument?.length) { return { - icon: IconLockOpen, + icon: IconUnlock, display: `Create Key "${argument}"`, executor: () => { personalAPIKeysLogic.actions.createKey(argument) @@ -689,7 +799,7 @@ export const commandPaletteLogic = kea([ key: 'create-dashboard', scope: GLOBAL_COMMAND_SCOPE, resolver: { - icon: IconGauge, + icon: IconDashboard, display: 'Create Dashboard', executor: () => ({ instruction: 'Name your new dashboard', @@ -698,7 +808,7 @@ export const commandPaletteLogic = kea([ resolver: (argument) => { if (argument?.length) { return { - icon: IconGauge, + icon: IconDashboard, display: `Create Dashboard "${argument}"`, executor: () => { newDashboardLogic.actions.addDashboard({ name: argument }) @@ -715,7 +825,7 @@ export const commandPaletteLogic = kea([ key: 'share-feedback', scope: GLOBAL_COMMAND_SCOPE, resolver: { - icon: IconComment, + icon: IconThoughtBubble, display: 'Share Feedback', synonyms: ['send opinion', 'ask question', 'message posthog', 'github issue'], executor: () => ({ @@ -723,12 +833,12 @@ export const commandPaletteLogic = kea([ resolver: [ { display: 'Send Message Directly to PostHog', - icon: IconComment, + icon: IconThoughtBubble, executor: () => ({ instruction: "What's on your mind?", - icon: IconComment, + icon: IconThoughtBubble, resolver: (argument) => ({ - icon: IconComment, + icon: IconThoughtBubble, display: 'Send', executor: !argument?.length ? undefined @@ -736,7 +846,7 @@ export const commandPaletteLogic = kea([ posthog.capture('palette feedback', { message: argument }) return { resolver: { - icon: IconCheckmark, + icon: IconCheck, display: 'Message Sent!', executor: true, }, @@ -757,6 +867,42 @@ export const commandPaletteLogic = kea([ }, } + const toggleTheme: Command = { + key: 'toggle-theme', + scope: GLOBAL_COMMAND_SCOPE, + resolver: { + icon: ThemeIcon, + display: 'Switch theme', + synonyms: ['toggle theme', 'dark mode', 'light mode'], + executor: () => ({ + scope: 'Switch theme', + resolver: [ + { + icon: IconDay, + display: 'Light theme', + executor: () => { + actions.overrideTheme(false) + }, + }, + { + icon: IconNight, + display: 'Dark theme', + executor: () => { + actions.overrideTheme(true) + }, + }, + { + icon: IconAsterisk, + display: 'Sync with system settings', + executor: () => { + actions.overrideTheme(null) + }, + }, + ], + }), + }, + } + actions.registerCommand(goTo) actions.registerCommand(openUrls) actions.registerCommand(debugClickhouseQueries) @@ -765,6 +911,9 @@ export const commandPaletteLogic = kea([ actions.registerCommand(createDashboard) actions.registerCommand(shareFeedback) actions.registerCommand(debugCopySessionRecordingURL) + if (values.featureFlags[FEATURE_FLAGS.POSTHOG_3000]) { + actions.registerCommand(toggleTheme) + } }, beforeUnmount: () => { actions.deregisterCommand('go-to') @@ -775,6 +924,7 @@ export const commandPaletteLogic = kea([ actions.deregisterCommand('create-dashboard') actions.deregisterCommand('share-feedback') actions.deregisterCommand('debug-copy-session-recording-url') + actions.deregisterCommand('toggle-theme') }, })), ]) diff --git a/frontend/src/lib/components/CompactList/CompactList.scss b/frontend/src/lib/components/CompactList/CompactList.scss index cd329a1d8e4f7..5c0e76c6093a3 100644 --- a/frontend/src/lib/components/CompactList/CompactList.scss +++ b/frontend/src/lib/components/CompactList/CompactList.scss @@ -1,7 +1,7 @@ .compact-list { border-radius: var(--radius); border: 1px solid var(--border); - height: calc(19.5rem + 1px); + height: calc(19.25rem); background: var(--bg-light); box-sizing: content-box; display: flex; @@ -22,13 +22,13 @@ } } - .spacer-container { - padding: 0 1rem; - } - .scrollable-list { flex: 1; - overflow: auto auto; - padding: 0 0.5rem 0.5rem; + overflow: auto; + padding: 0 0.5rem; + } + + .LemonButton { + font-family: var(--font-sans) !important; } } diff --git a/frontend/src/lib/components/CompactList/CompactList.stories.tsx b/frontend/src/lib/components/CompactList/CompactList.stories.tsx index dfce25599a81f..26d609a669a23 100644 --- a/frontend/src/lib/components/CompactList/CompactList.stories.tsx +++ b/frontend/src/lib/components/CompactList/CompactList.stories.tsx @@ -1,9 +1,9 @@ import { Meta } from '@storybook/react' - -import { CompactList } from './CompactList' -import { urls } from 'scenes/urls' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { PersonDisplay } from 'scenes/persons/PersonDisplay' +import { urls } from 'scenes/urls' + +import { CompactList } from './CompactList' const meta: Meta = { title: 'Components/Compact List', diff --git a/frontend/src/lib/components/CompactList/CompactList.tsx b/frontend/src/lib/components/CompactList/CompactList.tsx index 68cc77e0bb352..eaa3f392c1013 100644 --- a/frontend/src/lib/components/CompactList/CompactList.tsx +++ b/frontend/src/lib/components/CompactList/CompactList.tsx @@ -1,9 +1,11 @@ import './CompactList.scss' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' + import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { EmptyMessage, EmptyMessageProps } from '../EmptyMessage/EmptyMessage' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { EmptyMessage, EmptyMessageProps } from '../EmptyMessage/EmptyMessage' + interface CompactListProps { title: string viewAllURL?: string @@ -27,7 +29,7 @@ export function CompactList({

    {title}

    {viewAllURL && View all}
    -
    +
    diff --git a/frontend/src/lib/components/CompareFilter/CompareFilter.tsx b/frontend/src/lib/components/CompareFilter/CompareFilter.tsx index 02d2ad2e91f4b..a70f38cc31c17 100644 --- a/frontend/src/lib/components/CompareFilter/CompareFilter.tsx +++ b/frontend/src/lib/components/CompareFilter/CompareFilter.tsx @@ -1,6 +1,6 @@ -import { useValues, useActions } from 'kea' -import { insightLogic } from 'scenes/insights/insightLogic' import { LemonCheckbox } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' export function CompareFilter(): JSX.Element | null { diff --git a/frontend/src/lib/components/CopyToClipboard.tsx b/frontend/src/lib/components/CopyToClipboard.tsx index 0e85ee60317d8..03480644f2d5b 100644 --- a/frontend/src/lib/components/CopyToClipboard.tsx +++ b/frontend/src/lib/components/CopyToClipboard.tsx @@ -1,8 +1,8 @@ -import { HTMLProps } from 'react' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { IconCopy } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { HTMLProps } from 'react' interface InlinePropsBase extends HTMLProps { description?: string diff --git a/frontend/src/lib/components/DateDisplay/index.tsx b/frontend/src/lib/components/DateDisplay/index.tsx index c5e35fd8944e2..55a48230e6782 100644 --- a/frontend/src/lib/components/DateDisplay/index.tsx +++ b/frontend/src/lib/components/DateDisplay/index.tsx @@ -1,6 +1,8 @@ +import './DateDisplay.scss' + import { dayjs } from 'lib/dayjs' + import { IntervalType } from '~/types' -import './DateDisplay.scss' interface DateDisplayProps { date: string diff --git a/frontend/src/lib/components/DateFilter/DateFilter.tsx b/frontend/src/lib/components/DateFilter/DateFilter.tsx index 254493add06ed..d647324c7e1ee 100644 --- a/frontend/src/lib/components/DateFilter/DateFilter.tsx +++ b/frontend/src/lib/components/DateFilter/DateFilter.tsx @@ -1,15 +1,6 @@ -import { useRef } from 'react' -import { dateMapping, dateFilterToText, uuid } from 'lib/utils' -import { DateMappingOption } from '~/types' -import { dayjs } from 'lib/dayjs' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { dateFilterLogic } from './dateFilterLogic' -import { RollingDateRangeFilter } from './RollingDateRangeFilter' +import { Placement } from '@floating-ui/react' +import { LemonButton, LemonButtonProps, LemonButtonWithDropdown, LemonDivider } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonButtonWithDropdown, LemonDivider, LemonButton, LemonButtonProps } from '@posthog/lemon-ui' -import { IconCalendar } from 'lib/lemon-ui/icons' -import { LemonCalendarSelect } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' -import { LemonCalendarRange } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' import { CUSTOM_OPTION_DESCRIPTION, CUSTOM_OPTION_KEY, @@ -17,7 +8,18 @@ import { DateFilterLogicProps, DateFilterView, } from 'lib/components/DateFilter/types' -import { Placement } from '@floating-ui/react' +import { dayjs } from 'lib/dayjs' +import { IconCalendar } from 'lib/lemon-ui/icons' +import { LemonCalendarSelect } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' +import { LemonCalendarRange } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { dateFilterToText, dateMapping, uuid } from 'lib/utils' +import { useRef } from 'react' + +import { DateMappingOption } from '~/types' + +import { dateFilterLogic } from './dateFilterLogic' +import { RollingDateRangeFilter } from './RollingDateRangeFilter' export interface DateFilterProps { showCustom?: boolean diff --git a/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.scss b/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.scss index 8f99bb64c0bde..3d18b2e5b2d96 100644 --- a/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.scss +++ b/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.scss @@ -45,9 +45,15 @@ line-height: 1.25rem; align-items: center; - input { + .LemonInput { width: 3rem; - text-align: center; + min-height: 0; + padding: 0; + border: none; + + input { + text-align: center; + } } .RollingDateRangeFilter__counter__step { diff --git a/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.tsx b/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.tsx index c1371a66414fe..f25ef9f06be6d 100644 --- a/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.tsx +++ b/frontend/src/lib/components/DateFilter/RollingDateRangeFilter.tsx @@ -1,11 +1,12 @@ -import { Input } from 'antd' -import { DateOption, rollingDateRangeFilterLogic } from './rollingDateRangeFilterLogic' +import './RollingDateRangeFilter.scss' + +import { LemonButton, LemonInput, LemonSelect, LemonSelectOptions } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { LemonButton, LemonSelect, LemonSelectOptions } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { dayjs } from 'lib/dayjs' -import clsx from 'clsx' -import './RollingDateRangeFilter.scss' +import { Tooltip } from 'lib/lemon-ui/Tooltip' + +import { DateOption, rollingDateRangeFilterLogic } from './rollingDateRangeFilterLogic' const dateOptions: LemonSelectOptions = [ { value: 'days', label: 'days' }, @@ -38,11 +39,6 @@ export function RollingDateRangeFilter({ useActions(rollingDateRangeFilterLogic(logicProps)) const { counter, dateOption, formattedDate } = useValues(rollingDateRangeFilterLogic(logicProps)) - const onInputChange = (event: React.ChangeEvent): void => { - const newValue = event.target.value ? parseFloat(event.target.value) : undefined - setCounter(newValue) - } - return ( - - setCounter(value)} /> { let props: DateFilterLogicProps diff --git a/frontend/src/lib/components/DateFilter/dateFilterLogic.ts b/frontend/src/lib/components/DateFilter/dateFilterLogic.ts index 13536740e4c93..77faff7074618 100644 --- a/frontend/src/lib/components/DateFilter/dateFilterLogic.ts +++ b/frontend/src/lib/components/DateFilter/dateFilterLogic.ts @@ -1,9 +1,11 @@ -import { actions, props, kea, listeners, path, reducers, selectors, key } from 'kea' -import { dayjs, Dayjs } from 'lib/dayjs' -import type { dateFilterLogicType } from './dateFilterLogicType' -import { isDate, dateFilterToText, dateStringToDayJs, formatDateRange, formatDate } from 'lib/utils' -import { DateMappingOption } from '~/types' +import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { CUSTOM_OPTION_VALUE, DateFilterLogicProps, DateFilterView } from 'lib/components/DateFilter/types' +import { Dayjs, dayjs } from 'lib/dayjs' +import { dateFilterToText, dateStringToDayJs, formatDate, formatDateRange, isDate } from 'lib/utils' + +import { DateMappingOption } from '~/types' + +import type { dateFilterLogicType } from './dateFilterLogicType' export const dateFilterLogic = kea([ path(['lib', 'components', 'DateFilter', 'DateFilterLogic']), diff --git a/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.test.ts b/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.test.ts index f97bde9e94457..861274103fa93 100644 --- a/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.test.ts +++ b/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.test.ts @@ -1,5 +1,7 @@ import { expectLogic } from 'kea-test-utils' + import { initKeaTests } from '~/test/init' + import { rollingDateRangeFilterLogic } from './rollingDateRangeFilterLogic' describe('rollingDateRangeFilterLogic', () => { diff --git a/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.ts b/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.ts index 4600f12bc2b89..29bed83a7566f 100644 --- a/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.ts +++ b/frontend/src/lib/components/DateFilter/rollingDateRangeFilterLogic.ts @@ -1,9 +1,11 @@ -import { actions, props, kea, listeners, path, reducers, selectors } from 'kea' -import type { rollingDateRangeFilterLogicType } from './rollingDateRangeFilterLogicType' -import { Dayjs } from 'lib/dayjs' import './RollingDateRangeFilter.scss' + +import { actions, kea, listeners, path, props, reducers, selectors } from 'kea' +import { Dayjs } from 'lib/dayjs' import { dateFilterToText } from 'lib/utils' +import type { rollingDateRangeFilterLogicType } from './rollingDateRangeFilterLogicType' + const dateOptionsMap = { q: 'quarters', m: 'months', diff --git a/frontend/src/lib/components/DateFilter/types.ts b/frontend/src/lib/components/DateFilter/types.ts index 63bbd2c29303a..d3563cbbad0bf 100644 --- a/frontend/src/lib/components/DateFilter/types.ts +++ b/frontend/src/lib/components/DateFilter/types.ts @@ -1,4 +1,5 @@ import { Dayjs } from 'lib/dayjs' + import { DateMappingOption } from '~/types' export enum DateFilterView { diff --git a/frontend/src/lib/components/DatePicker.tsx b/frontend/src/lib/components/DatePicker.tsx index dfe194d66ab78..42ce83e78640a 100644 --- a/frontend/src/lib/components/DatePicker.tsx +++ b/frontend/src/lib/components/DatePicker.tsx @@ -1,6 +1,7 @@ -import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs' -import generatePicker from 'antd/lib/date-picker/generatePicker' import './DatePicker.scss' + +import generatePicker from 'antd/lib/date-picker/generatePicker' import { dayjs } from 'lib/dayjs' +import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs' export const DatePicker = generatePicker(dayjsGenerateConfig) diff --git a/frontend/src/lib/components/DebugNotice.tsx b/frontend/src/lib/components/DebugNotice.tsx index 4ceb631d56d77..0f6f31011e404 100644 --- a/frontend/src/lib/components/DebugNotice.tsx +++ b/frontend/src/lib/components/DebugNotice.tsx @@ -1,6 +1,6 @@ -import { useEffect, useState } from 'react' import { IconClose } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useEffect, useState } from 'react' export function DebugNotice(): JSX.Element | null { const [debugInfo, setDebugInfo] = useState<{ branch: string; revision: string } | undefined>() diff --git a/frontend/src/lib/components/DefinitionPopover/ActionPopoverInfo.tsx b/frontend/src/lib/components/DefinitionPopover/ActionPopoverInfo.tsx index 1ad4be54568a5..b0e9045a7e102 100644 --- a/frontend/src/lib/components/DefinitionPopover/ActionPopoverInfo.tsx +++ b/frontend/src/lib/components/DefinitionPopover/ActionPopoverInfo.tsx @@ -1,6 +1,8 @@ -import { ActionType } from '~/types' import { DefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopover' import { genericOperatorToHumanName, propertyValueToHumanName } from 'lib/components/DefinitionPopover/utils' + +import { ActionType } from '~/types' + import { PropertyKeyInfo } from '../PropertyKeyInfo' export function ActionPopoverInfo({ entity }: { entity: ActionType }): JSX.Element | null { diff --git a/frontend/src/lib/components/DefinitionPopover/CohortPopoverInfo.tsx b/frontend/src/lib/components/DefinitionPopover/CohortPopoverInfo.tsx index 1c309951b1956..2cfcf0ad93948 100644 --- a/frontend/src/lib/components/DefinitionPopover/CohortPopoverInfo.tsx +++ b/frontend/src/lib/components/DefinitionPopover/CohortPopoverInfo.tsx @@ -1,21 +1,23 @@ -import { AnyCohortCriteriaType, CohortType, FilterLogicalOperator } from '~/types' +import { useValues } from 'kea' import { DefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopover' import { genericOperatorToHumanName, operatorToHumanName, propertyValueToHumanName, } from 'lib/components/DefinitionPopover/utils' +import { pluralize } from 'lib/utils' +import { BEHAVIORAL_TYPE_TO_LABEL } from 'scenes/cohorts/CohortFilters/constants' import { COHORT_MATCHING_DAYS, criteriaToBehavioralFilterType, criteriaToHumanSentence, isCohortCriteriaGroup, } from 'scenes/cohorts/cohortUtils' -import { pluralize } from 'lib/utils' -import { BEHAVIORAL_TYPE_TO_LABEL } from 'scenes/cohorts/CohortFilters/constants' -import { useValues } from 'kea' -import { cohortsModel } from '~/models/cohortsModel' + import { actionsModel } from '~/models/actionsModel' +import { cohortsModel } from '~/models/cohortsModel' +import { AnyCohortCriteriaType, CohortType, FilterLogicalOperator } from '~/types' + import { PropertyKeyInfo } from '../PropertyKeyInfo' const MAX_CRITERIA_GROUPS = 2 diff --git a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss index 46fc5d0773ed8..4f9297fe9261b 100644 --- a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss +++ b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.scss @@ -146,15 +146,6 @@ .definition-popover-edit-form-value { margin-bottom: 1rem; - - &.definition-popover-owner-select { - .ant-select-selector { - .ant-select-selection-placeholder { - color: black; - font-weight: normal; - } - } - } } } } diff --git a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.tsx b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.tsx index 65a7711d3b635..db1cdb0b48cdc 100644 --- a/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.tsx +++ b/frontend/src/lib/components/DefinitionPopover/DefinitionPopover.tsx @@ -1,18 +1,19 @@ import './DefinitionPopover.scss' + +import { Divider, DividerProps } from 'antd' import clsx from 'clsx' -import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' import { useActions, useValues } from 'kea' +import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { getKeyMapping } from 'lib/taxonomy' -import { KeyMapping, UserBasicType, PropertyDefinition } from '~/types' -import { Owner } from 'scenes/events/Owner' import { dayjs } from 'lib/dayjs' -import { Divider, DividerProps, Select } from 'antd' -import { membersLogic } from 'scenes/organization/membersLogic' +import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' import { Link } from 'lib/lemon-ui/Link' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { getKeyMapping } from 'lib/taxonomy' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { Owner } from 'scenes/events/Owner' + +import { KeyMapping, UserBasicType } from '~/types' interface DefinitionPopoverProps { children: React.ReactNode @@ -236,48 +237,6 @@ function Card({ ) } -function Type({ propertyType }: { propertyType: PropertyDefinition['property_type'] | null }): JSX.Element { - return propertyType ? ( -
    -
    {propertyType}
    -
    - ) : ( - <> - ) -} - -function OwnerDropdown(): JSX.Element { - const { members } = useValues(membersLogic) - const { localDefinition } = useValues(definitionPopoverLogic) - const { setLocalDefinition } = useActions(definitionPopoverLogic) - - return ( - - ) -} - export const DefinitionPopover = { Wrapper, Header, @@ -289,6 +248,4 @@ export const DefinitionPopover = { Grid, Section, Card, - OwnerDropdown, - Type, } diff --git a/frontend/src/lib/components/DefinitionPopover/DefinitionPopoverContents.tsx b/frontend/src/lib/components/DefinitionPopover/DefinitionPopoverContents.tsx index cb4af914b655b..7193044205f95 100644 --- a/frontend/src/lib/components/DefinitionPopover/DefinitionPopoverContents.tsx +++ b/frontend/src/lib/components/DefinitionPopover/DefinitionPopoverContents.tsx @@ -1,26 +1,28 @@ +import { hide } from '@floating-ui/react' +import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { ActionPopoverInfo } from 'lib/components/DefinitionPopover/ActionPopoverInfo' +import { CohortPopoverInfo } from 'lib/components/DefinitionPopover/CohortPopoverInfo' +import { DefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopover' +import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { SimpleOption, TaxonomicDefinitionTypes, TaxonomicFilterGroup, TaxonomicFilterGroupType, } from 'lib/components/TaxonomicFilter/types' -import { useActions, useValues } from 'kea' -import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' -import { useEffect } from 'react' -import { isPostHogProp, KEY_MAPPING } from 'lib/taxonomy' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { DefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopover' -import { Link } from 'lib/lemon-ui/Link' import { IconInfo, IconLock, IconOpenInNew } from 'lib/lemon-ui/icons' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { ActionType, CohortType, EventDefinition, PropertyDefinition } from '~/types' -import { ActionPopoverInfo } from 'lib/components/DefinitionPopover/ActionPopoverInfo' -import { CohortPopoverInfo } from 'lib/components/DefinitionPopover/CohortPopoverInfo' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { Link } from 'lib/lemon-ui/Link' import { Popover } from 'lib/lemon-ui/Popover' -import { hide } from '@floating-ui/react' -import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { isPostHogProp, KEY_MAPPING } from 'lib/taxonomy' +import { useEffect } from 'react' + +import { ActionType, CohortType, EventDefinition, PropertyDefinition } from '~/types' + import { TZLabel } from '../TZLabel' function TaxonomyIntroductionSection(): JSX.Element { diff --git a/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.test.ts b/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.test.ts index c893dc7506426..1db1f33132221 100644 --- a/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.test.ts +++ b/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.test.ts @@ -1,5 +1,14 @@ -import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' +import { expectLogic } from 'kea-test-utils' import api from 'lib/api' +import { definitionPopoverLogic, DefinitionPopoverState } from 'lib/components/DefinitionPopover/definitionPopoverLogic' +import { TaxonomicDefinitionTypes, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { urls } from 'scenes/urls' + +import { useMocks } from '~/mocks/jest' +import { actionsModel } from '~/models/actionsModel' +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { initKeaTests } from '~/test/init' import { mockActionDefinition, mockCohort, @@ -9,15 +18,7 @@ import { mockGroup, mockPersonProperty, } from '~/test/mocks' -import { initKeaTests } from '~/test/init' -import { TaxonomicDefinitionTypes, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { expectLogic } from 'kea-test-utils' -import { urls } from 'scenes/urls' -import { actionsModel } from '~/models/actionsModel' import { ActionType, CohortType, PersonProperty, PropertyDefinition } from '~/types' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { cohortsModel } from '~/models/cohortsModel' -import { useMocks } from '~/mocks/jest' describe('definitionPopoverLogic', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.ts b/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.ts index 931d1b00114d6..38f907c0abbee 100644 --- a/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.ts +++ b/frontend/src/lib/components/DefinitionPopover/definitionPopoverLogic.ts @@ -1,19 +1,21 @@ +import equal from 'fast-deep-equal' +import { actions, connect, events, kea, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import type { definitionPopoverLogicType } from './definitionPopoverLogicType' +import api from 'lib/api' +import { getSingularType } from 'lib/components/DefinitionPopover/utils' import { TaxonomicDefinitionTypes, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { capitalizeFirstLetter } from 'lib/utils' -import { getSingularType } from 'lib/components/DefinitionPopover/utils' -import { ActionType, AvailableFeature, CohortType, EventDefinition, PropertyDefinition } from '~/types' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { urls } from 'scenes/urls' -import api from 'lib/api' +import { userLogic } from 'scenes/userLogic' + import { actionsModel } from '~/models/actionsModel' -import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' import { cohortsModel } from '~/models/cohortsModel' -import equal from 'fast-deep-equal' -import { userLogic } from 'scenes/userLogic' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' +import { ActionType, AvailableFeature, CohortType, EventDefinition, PropertyDefinition } from '~/types' + +import type { definitionPopoverLogicType } from './definitionPopoverLogicType' const IS_TEST_MODE = process.env.NODE_ENV === 'test' diff --git a/frontend/src/lib/components/DefinitionPopover/utils.ts b/frontend/src/lib/components/DefinitionPopover/utils.ts index 1a7ec529cc0f3..f828aea981fc2 100644 --- a/frontend/src/lib/components/DefinitionPopover/utils.ts +++ b/frontend/src/lib/components/DefinitionPopover/utils.ts @@ -1,7 +1,8 @@ -import { AnyPropertyFilter, PropertyFilterValue, PropertyOperator } from '~/types' -import { allOperatorsMapping, genericOperatorMap } from 'lib/utils' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { isPropertyFilterWithOperator } from 'lib/components/PropertyFilters/utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { allOperatorsMapping, genericOperatorMap } from 'lib/utils' + +import { AnyPropertyFilter, PropertyFilterValue, PropertyOperator } from '~/types' export function operatorToHumanName(operator?: string): string { if (operator === 'gte') { diff --git a/frontend/src/lib/components/Drawer.tsx b/frontend/src/lib/components/Drawer.tsx index 2275e52121054..bc20fba4bff30 100644 --- a/frontend/src/lib/components/Drawer.tsx +++ b/frontend/src/lib/components/Drawer.tsx @@ -1,6 +1,6 @@ -import { PropsWithChildren } from 'react' import { Drawer as AntDrawer } from 'antd' import { DrawerProps } from 'antd/lib/drawer' +import { PropsWithChildren } from 'react' export function Drawer(props: PropsWithChildren): JSX.Element { return diff --git a/frontend/src/lib/components/DropdownSelector/DropdownSelector.scss b/frontend/src/lib/components/DropdownSelector/DropdownSelector.scss deleted file mode 100644 index b8ffa8d145235..0000000000000 --- a/frontend/src/lib/components/DropdownSelector/DropdownSelector.scss +++ /dev/null @@ -1,25 +0,0 @@ -.dropdown-selector { - padding: 0.5rem; - border: 1px solid var(--border-light); - border-radius: var(--radius); - display: flex; - align-items: center; - cursor: pointer; - - &.disabled { - color: var(--muted); - cursor: not-allowed; - } - - &.compact { - padding: 0.333rem 0.5rem; - } - - .dropdown-arrow { - display: flex; - align-items: center; - padding-left: 4px; - font-size: 1.2em; - color: var(--muted-alt); - } -} diff --git a/frontend/src/lib/components/DropdownSelector/DropdownSelector.tsx b/frontend/src/lib/components/DropdownSelector/DropdownSelector.tsx deleted file mode 100644 index abe0e061c8827..0000000000000 --- a/frontend/src/lib/components/DropdownSelector/DropdownSelector.tsx +++ /dev/null @@ -1,94 +0,0 @@ -/* Custom dropdown selector with an icon a help caption */ -import { Dropdown, Menu } from 'antd' -import clsx from 'clsx' -import { IconArrowDropDown } from 'lib/lemon-ui/icons' -import './DropdownSelector.scss' - -interface DropdownSelectorProps { - label?: string - value: string | null - onValueChange: (value: string) => void - options: DropdownOption[] - hideDescriptionOnDisplay?: boolean // Hides the description support text on the main display component (i.e. only shown in the dropdown menu) - disabled?: boolean - compact?: boolean -} - -interface DropdownOption { - key: string - label: string - description?: string - icon: JSX.Element - hidden?: boolean -} - -interface SelectItemInterface { - icon: JSX.Element - label: string - description?: string - onClick: () => void -} - -function SelectItem({ icon, label, description, onClick }: SelectItemInterface): JSX.Element { - return ( -
    -
    - {icon} -
    {label}
    -
    - {description &&
    {description}
    } -
    - ) -} - -export function DropdownSelector({ - label, - value, - onValueChange, - options, - hideDescriptionOnDisplay, - disabled, - compact, -}: DropdownSelectorProps): JSX.Element { - const selectedOption = options.find((opt) => opt.key === value) - - const menu = ( - - {options.map(({ key, hidden, ...props }) => { - if (hidden) { - return null - } - return ( - - onValueChange(key)} /> - - ) - })} - - ) - - return ( - <> - {label && } - -
    e.preventDefault()} - > -
    - {selectedOption && ( - {}} - description={hideDescriptionOnDisplay ? undefined : selectedOption.description} - /> - )} -
    -
    - -
    -
    -
    - - ) -} diff --git a/frontend/src/lib/components/DurationPicker/DurationPicker.tsx b/frontend/src/lib/components/DurationPicker/DurationPicker.tsx index bf4829b86bb4e..234cce3fe91c4 100644 --- a/frontend/src/lib/components/DurationPicker/DurationPicker.tsx +++ b/frontend/src/lib/components/DurationPicker/DurationPicker.tsx @@ -1,6 +1,7 @@ +import { LemonInput, LemonSelect } from '@posthog/lemon-ui' import { useEffect, useState } from 'react' + import { Duration, SmallTimeUnit } from '~/types' -import { LemonSelect, LemonInput } from '@posthog/lemon-ui' interface DurationPickerProps { onChange: (value_seconds: number) => void diff --git a/frontend/src/lib/components/EditableField/EditableField.stories.tsx b/frontend/src/lib/components/EditableField/EditableField.stories.tsx index 50b2d02b1cba8..47452fb1f9584 100644 --- a/frontend/src/lib/components/EditableField/EditableField.stories.tsx +++ b/frontend/src/lib/components/EditableField/EditableField.stories.tsx @@ -1,7 +1,7 @@ import { Meta, StoryFn } from '@storybook/react' +import { useState } from 'react' import { EditableField as EditableFieldComponent } from './EditableField' -import { useState } from 'react' const meta: Meta = { title: 'Components/Editable Field', diff --git a/frontend/src/lib/components/EditableField/EditableField.tsx b/frontend/src/lib/components/EditableField/EditableField.tsx index 2070ea7f1cbe3..369af42b55c30 100644 --- a/frontend/src/lib/components/EditableField/EditableField.tsx +++ b/frontend/src/lib/components/EditableField/EditableField.tsx @@ -1,12 +1,13 @@ -import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' import './EditableField.scss' + +import clsx from 'clsx' import { IconEdit, IconMarkdown } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import TextareaAutosize from 'react-textarea-autosize' -import clsx from 'clsx' -import { pluralize } from 'lib/utils' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { pluralize } from 'lib/utils' +import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react' +import TextareaAutosize from 'react-textarea-autosize' export interface EditableFieldProps { /** What this field stands for. */ diff --git a/frontend/src/lib/components/EmptyMessage/EmptyMessage.tsx b/frontend/src/lib/components/EmptyMessage/EmptyMessage.tsx index 5635871c84a77..c60ae07df27b7 100644 --- a/frontend/src/lib/components/EmptyMessage/EmptyMessage.tsx +++ b/frontend/src/lib/components/EmptyMessage/EmptyMessage.tsx @@ -1,4 +1,5 @@ import './EmptyMessage.scss' + import { LemonButton } from 'lib/lemon-ui/LemonButton' export interface EmptyMessageProps { diff --git a/frontend/src/lib/components/EntityFilterInfo.tsx b/frontend/src/lib/components/EntityFilterInfo.tsx index e1e628b61951d..4c3753a461132 100644 --- a/frontend/src/lib/components/EntityFilterInfo.tsx +++ b/frontend/src/lib/components/EntityFilterInfo.tsx @@ -1,7 +1,8 @@ -import { ActionFilter, EntityFilter } from '~/types' +import clsx from 'clsx' import { getKeyMapping } from 'lib/taxonomy' import { getDisplayNameFromEntityFilter, isAllEventsEntityFilter } from 'scenes/insights/utils' -import clsx from 'clsx' + +import { ActionFilter, EntityFilter } from '~/types' interface EntityFilterInfoProps { filter: EntityFilter | ActionFilter diff --git a/frontend/src/lib/components/Errors/ErrorDisplay.stories.tsx b/frontend/src/lib/components/Errors/ErrorDisplay.stories.tsx index bd8ffc5935d81..86915996ae5f6 100644 --- a/frontend/src/lib/components/Errors/ErrorDisplay.stories.tsx +++ b/frontend/src/lib/components/Errors/ErrorDisplay.stories.tsx @@ -1,5 +1,6 @@ import { Meta } from '@storybook/react' import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay' + import { EventType, RecordingEventType } from '~/types' const meta: Meta = { diff --git a/frontend/src/lib/components/Errors/ErrorDisplay.tsx b/frontend/src/lib/components/Errors/ErrorDisplay.tsx index 4c14a6e44412a..9b8c461090f42 100644 --- a/frontend/src/lib/components/Errors/ErrorDisplay.tsx +++ b/frontend/src/lib/components/Errors/ErrorDisplay.tsx @@ -1,10 +1,11 @@ -import { EventType, RecordingEventType } from '~/types' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { IconFlag } from 'lib/lemon-ui/icons' import clsx from 'clsx' +import { IconFlag } from 'lib/lemon-ui/icons' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { Link } from 'lib/lemon-ui/Link' import posthog from 'posthog-js' +import { EventType, RecordingEventType } from '~/types' + interface StackFrame { filename: string lineno: number diff --git a/frontend/src/lib/components/EventSelect/EventSelect.stories.tsx b/frontend/src/lib/components/EventSelect/EventSelect.stories.tsx index b13517b039dd5..767397adbf7eb 100644 --- a/frontend/src/lib/components/EventSelect/EventSelect.stories.tsx +++ b/frontend/src/lib/components/EventSelect/EventSelect.stories.tsx @@ -1,6 +1,8 @@ import { Meta } from '@storybook/react' import { useState } from 'react' + import { mswDecorator } from '~/mocks/browser' + import { EventSelect } from './EventSelect' const eventDefinitions = [ diff --git a/frontend/src/lib/components/EventSelect/EventSelect.tsx b/frontend/src/lib/components/EventSelect/EventSelect.tsx index fa988eee99b84..fe4db309d44bc 100644 --- a/frontend/src/lib/components/EventSelect/EventSelect.tsx +++ b/frontend/src/lib/components/EventSelect/EventSelect.tsx @@ -1,7 +1,7 @@ -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' -import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' +import { Popover } from 'lib/lemon-ui/Popover/Popover' import React, { useState } from 'react' interface EventSelectProps { diff --git a/frontend/src/lib/components/ExportButton/ExportButton.tsx b/frontend/src/lib/components/ExportButton/ExportButton.tsx index 2468fc60c1992..6f2b40f8d0628 100644 --- a/frontend/src/lib/components/ExportButton/ExportButton.tsx +++ b/frontend/src/lib/components/ExportButton/ExportButton.tsx @@ -1,6 +1,8 @@ -import { ExporterFormat, OnlineExportContext } from '~/types' import { LemonButton, LemonButtonProps, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' + +import { ExporterFormat, OnlineExportContext } from '~/types' + import { triggerExport, TriggerExportProps } from './exporter' export interface ExportButtonItem { diff --git a/frontend/src/lib/components/ExportButton/exporter.tsx b/frontend/src/lib/components/ExportButton/exporter.tsx index 3ff134f766ecb..97cff3343e00c 100644 --- a/frontend/src/lib/components/ExportButton/exporter.tsx +++ b/frontend/src/lib/components/ExportButton/exporter.tsx @@ -1,13 +1,14 @@ +import { AnimationType } from 'lib/animations/animations' import api from 'lib/api' +import { Animation } from 'lib/components/Animation/Animation' +import { dayjs } from 'lib/dayjs' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { delay } from 'lib/utils' import posthog from 'posthog-js' -import { ExportContext, ExportedAssetType, ExporterFormat, LocalExportContext } from '~/types' -import { lemonToast } from 'lib/lemon-ui/lemonToast' import { useEffect, useState } from 'react' -import { AnimationType } from 'lib/animations/animations' -import { Animation } from 'lib/components/Animation/Animation' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { dayjs } from 'lib/dayjs' + +import { ExportContext, ExportedAssetType, ExporterFormat, LocalExportContext } from '~/types' const POLL_DELAY_MS = 1000 const MAX_PNG_POLL = 10 diff --git a/frontend/src/lib/components/Fade/Fade.tsx b/frontend/src/lib/components/Fade/Fade.tsx index cc4637bb26957..2ac4faad950c7 100644 --- a/frontend/src/lib/components/Fade/Fade.tsx +++ b/frontend/src/lib/components/Fade/Fade.tsx @@ -1,4 +1,5 @@ import './Fade.scss' + import { useEffect, useState } from 'react' export function Fade({ diff --git a/frontend/src/lib/components/FilterPropertyLink.tsx b/frontend/src/lib/components/FilterPropertyLink.tsx index 7db8edfe9ef76..a6253218953e6 100644 --- a/frontend/src/lib/components/FilterPropertyLink.tsx +++ b/frontend/src/lib/components/FilterPropertyLink.tsx @@ -1,9 +1,9 @@ import { combineUrl } from 'kea-router' - import { Property } from 'lib/components/Property' +import { parseProperties } from 'lib/components/PropertyFilters/utils' import { Link } from 'lib/lemon-ui/Link' + import { FilterType } from '~/types' -import { parseProperties } from 'lib/components/PropertyFilters/utils' export function FilterPropertyLink({ property, diff --git a/frontend/src/lib/components/FlagSelector.tsx b/frontend/src/lib/components/FlagSelector.tsx index 2c14b90d98b03..15c5dfb02f069 100644 --- a/frontend/src/lib/components/FlagSelector.tsx +++ b/frontend/src/lib/components/FlagSelector.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' import { useValues } from 'kea' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' -import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps } from 'lib/components/TaxonomicFilter/types' -import { Popover } from 'lib/lemon-ui/Popover' import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' +import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps } from 'lib/components/TaxonomicFilter/types' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Popover } from 'lib/lemon-ui/Popover' +import { useState } from 'react' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' interface FlagSelectorProps { value: number | undefined diff --git a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories.tsx b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories.tsx index 7a234ce903e49..451f5d7a84c10 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories.tsx +++ b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories.tsx @@ -1,5 +1,7 @@ import { Meta } from '@storybook/react' + import { ElementType } from '~/types' + import { HTMLElementsDisplay } from './HTMLElementsDisplay' const meta: Meta = { diff --git a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx index efb03c2927016..73f5a6e0d0544 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx +++ b/frontend/src/lib/components/HTMLElementsDisplay/HTMLElementsDisplay.tsx @@ -1,12 +1,14 @@ -import { ElementType } from '~/types' -import { SelectableElement } from './SelectableElement' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { htmlElementsDisplayLogic } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic' import { useActions, useValues } from 'kea' -import { useState } from 'react' import { CodeSnippet } from 'lib/components/CodeSnippet' +import { htmlElementsDisplayLogic } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic' import { ParsedCSSSelector } from 'lib/components/HTMLElementsDisplay/preselectWithCSS' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { useState } from 'react' + +import { ElementType } from '~/types' + import { Fade } from '../Fade/Fade' +import { SelectableElement } from './SelectableElement' function indent(level: number): string { return Array(level).fill(' ').join('') diff --git a/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.tsx b/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.tsx index 37aa45c5f5710..805a02a1042f1 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.tsx +++ b/frontend/src/lib/components/HTMLElementsDisplay/SelectableElement.tsx @@ -1,9 +1,11 @@ -import { ElementType } from '~/types' -import clsx from 'clsx' import './SelectableElement.scss' + +import clsx from 'clsx' import { ParsedCSSSelector } from 'lib/components/HTMLElementsDisplay/preselectWithCSS' import { objectsEqual } from 'lib/utils' +import { ElementType } from '~/types' + export function TagPart({ tagName, selectedParts, diff --git a/frontend/src/lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic.ts b/frontend/src/lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic.ts index 12d73a9792a86..80b8d769ff2a5 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic.ts +++ b/frontend/src/lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic.ts @@ -1,14 +1,15 @@ import { actions, kea, key, path, props, propsChanged, reducers, selectors } from 'kea' - -import type { htmlElementsDisplayLogicType } from './htmlElementsDisplayLogicType' -import { ElementType } from '~/types' -import { objectsEqual } from 'lib/utils' +import { subscriptions } from 'kea-subscriptions' import { ParsedCSSSelector, parsedSelectorToSelectorString, preselect, } from 'lib/components/HTMLElementsDisplay/preselectWithCSS' -import { subscriptions } from 'kea-subscriptions' +import { objectsEqual } from 'lib/utils' + +import { ElementType } from '~/types' + +import type { htmlElementsDisplayLogicType } from './htmlElementsDisplayLogicType' export interface HtmlElementDisplayLogicProps { checkUniqueness: boolean diff --git a/frontend/src/lib/components/HTMLElementsDisplay/preselectWithCSS.test.ts b/frontend/src/lib/components/HTMLElementsDisplay/preselectWithCSS.test.ts index 03fffb3b47f33..31fc2adb144b5 100644 --- a/frontend/src/lib/components/HTMLElementsDisplay/preselectWithCSS.test.ts +++ b/frontend/src/lib/components/HTMLElementsDisplay/preselectWithCSS.test.ts @@ -1,12 +1,13 @@ -import { ElementType } from '~/types' +import { EXAMPLE_ELEMENTS } from 'lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories' +import { elementsChain } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic' import { - parseCSSSelector, matchesSelector, - preselect, + parseCSSSelector, parsedSelectorToSelectorString, + preselect, } from 'lib/components/HTMLElementsDisplay/preselectWithCSS' -import { EXAMPLE_ELEMENTS } from 'lib/components/HTMLElementsDisplay/HTMLElementsDisplay.stories' -import { elementsChain } from 'lib/components/HTMLElementsDisplay/htmlElementsDisplayLogic' + +import { ElementType } from '~/types' const elements = [ { diff --git a/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.stories.tsx b/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.stories.tsx index 63e648a6b07f2..a8b5efdd4cfc8 100644 --- a/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.stories.tsx +++ b/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.stories.tsx @@ -1,12 +1,11 @@ import { Meta, StoryFn } from '@storybook/react' + import { HedgehogBuddy } from './HedgehogBuddy' const meta: Meta = { title: 'Components/Hedgehog Buddy', component: HedgehogBuddy, - parameters: { - testOptions: { skip: true }, // Hedgehogs aren't particularly snapshotable - }, + tags: ['test-skip'], // Hedgehogs aren't particularly snapshotable } export default meta diff --git a/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.tsx b/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.tsx index c777e1d06c0a9..d4974f649824a 100644 --- a/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.tsx +++ b/frontend/src/lib/components/HedgehogBuddy/HedgehogBuddy.tsx @@ -1,27 +1,29 @@ -import { MutableRefObject, useEffect, useRef, useState } from 'react' +import './HedgehogBuddy.scss' -import { capitalizeFirstLetter, range, sampleOne } from 'lib/utils' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { useActions, useValues } from 'kea' -import { hedgehogbuddyLogic } from './hedgehogbuddyLogic' +import { FEATURE_FLAGS } from 'lib/constants' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { capitalizeFirstLetter, range, sampleOne } from 'lib/utils' +import { MutableRefObject, useEffect, useRef, useState } from 'react' + +import { themeLogic } from '~/layout/navigation-3000/themeLogic' + +import { FlaggedFeature } from '../FlaggedFeature' +import { HedgehogBuddyAccessory } from './components/AccessoryButton' +import { hedgehogbuddyLogic } from './hedgehogbuddyLogic' import { + accessoryGroups, + AccessoryInfo, + baseSpriteAccessoriesPath, + baseSpritePath, SHADOW_HEIGHT, SPRITE_SHEET_WIDTH, SPRITE_SIZE, - standardAnimations, standardAccessories, - AccessoryInfo, - accessoryGroups, - baseSpritePath, - baseSpriteAccessoriesPath, + standardAnimations, } from './sprites/sprites' -import { FlaggedFeature } from '../FlaggedFeature' -import { FEATURE_FLAGS } from 'lib/constants' -import { HedgehogBuddyAccessory } from './components/AccessoryButton' -import './HedgehogBuddy.scss' -import { themeLogic } from '~/layout/navigation-3000/themeLogic' const xFrames = SPRITE_SHEET_WIDTH / SPRITE_SIZE const boundaryPadding = 20 diff --git a/frontend/src/lib/components/HedgehogBuddy/components/AccessoryButton.tsx b/frontend/src/lib/components/HedgehogBuddy/components/AccessoryButton.tsx index 2d62f3242945a..852a790607bc1 100644 --- a/frontend/src/lib/components/HedgehogBuddy/components/AccessoryButton.tsx +++ b/frontend/src/lib/components/HedgehogBuddy/components/AccessoryButton.tsx @@ -1,10 +1,12 @@ -import { capitalizeFirstLetter } from 'lib/utils' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { useActions, useValues } from 'kea' import { IconLock } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { capitalizeFirstLetter } from 'lib/utils' + +import { themeLogic } from '~/layout/navigation-3000/themeLogic' + import { hedgehogbuddyLogic } from '../hedgehogbuddyLogic' import { AccessoryInfo, baseSpriteAccessoriesPath } from '../sprites/sprites' -import { themeLogic } from '~/layout/navigation-3000/themeLogic' export type HedgehogBuddyAccessoryProps = { accessory: AccessoryInfo diff --git a/frontend/src/lib/components/HedgehogBuddy/hedgehogbuddyLogic.ts b/frontend/src/lib/components/HedgehogBuddy/hedgehogbuddyLogic.ts index e2c1215290f2f..e010b8914077c 100644 --- a/frontend/src/lib/components/HedgehogBuddy/hedgehogbuddyLogic.ts +++ b/frontend/src/lib/components/HedgehogBuddy/hedgehogbuddyLogic.ts @@ -1,7 +1,7 @@ import { actions, kea, listeners, path, reducers, selectors } from 'kea' +import posthog from 'posthog-js' import type { hedgehogbuddyLogicType } from './hedgehogbuddyLogicType' -import posthog from 'posthog-js' import { AccessoryInfo, standardAccessories } from './sprites/sprites' export const hedgehogbuddyLogic = kea([ diff --git a/frontend/src/lib/components/HelpButton/HelpButton.tsx b/frontend/src/lib/components/HelpButton/HelpButton.tsx index 60fdb6f44e2d5..18ce8adba63e7 100644 --- a/frontend/src/lib/components/HelpButton/HelpButton.tsx +++ b/frontend/src/lib/components/HelpButton/HelpButton.tsx @@ -1,25 +1,28 @@ import './HelpButton.scss' -import { kea, useActions, useValues, props, key, path, connect, actions, reducers, listeners } from 'kea' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { HelpType } from '~/types' -import type { helpButtonLogicType } from './HelpButtonType' + +import { Placement } from '@floating-ui/react' +import clsx from 'clsx' +import { actions, connect, kea, key, listeners, path, props, reducers, useActions, useValues } from 'kea' import { IconArrowDropDown, IconArticle, + IconBugReport, + IconFeedback, IconHelpOutline, - IconQuestionAnswer, IconMessages, + IconQuestionAnswer, IconSupport, - IconFeedback, - IconBugReport, } from 'lib/lemon-ui/icons' -import clsx from 'clsx' -import { Placement } from '@floating-ui/react' +import { LemonMenu } from 'lib/lemon-ui/LemonMenu' import { DefaultAction, inAppPromptLogic } from 'lib/logic/inAppPrompt/inAppPromptLogic' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { HelpType } from '~/types' + import { supportLogic } from '../Support/supportLogic' import { SupportModal } from '../Support/SupportModal' -import { LemonMenu } from 'lib/lemon-ui/LemonMenu' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import type { helpButtonLogicType } from './HelpButtonType' const HELP_UTM_TAGS = '?utm_medium=in-product&utm_campaign=help-button-top' diff --git a/frontend/src/lib/components/HogQLEditor/HogQLEditor.stories.tsx b/frontend/src/lib/components/HogQLEditor/HogQLEditor.stories.tsx index e05346f298650..43fc695208b90 100644 --- a/frontend/src/lib/components/HogQLEditor/HogQLEditor.stories.tsx +++ b/frontend/src/lib/components/HogQLEditor/HogQLEditor.stories.tsx @@ -1,7 +1,8 @@ -import { StoryFn, Meta, StoryObj } from '@storybook/react' -import { HogQLEditor } from './HogQLEditor' +import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useState } from 'react' +import { HogQLEditor } from './HogQLEditor' + type Story = StoryObj const meta: Meta = { title: 'Components/HogQLEditor', diff --git a/frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx b/frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx index 0177f2345311c..44bea60e144d4 100644 --- a/frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx +++ b/frontend/src/lib/components/HogQLEditor/HogQLEditor.tsx @@ -1,11 +1,12 @@ -import { useRef, useState } from 'react' -import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { CLICK_OUTSIDE_BLOCK_CLASS } from 'lib/hooks/useOutsideClickHandler' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconErrorOutline, IconInfo } from 'lib/lemon-ui/icons' -import { useActions, useValues } from 'kea' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { useRef, useState } from 'react' + import { hogQLEditorLogic } from './hogQLEditorLogic' -import { Link } from '@posthog/lemon-ui' export interface HogQLEditorProps { onChange: (value: string) => void diff --git a/frontend/src/lib/components/HogQLEditor/hogQLEditorLogic.ts b/frontend/src/lib/components/HogQLEditor/hogQLEditorLogic.ts index 0507e78595775..9e6ce67595653 100644 --- a/frontend/src/lib/components/HogQLEditor/hogQLEditorLogic.ts +++ b/frontend/src/lib/components/HogQLEditor/hogQLEditorLogic.ts @@ -1,10 +1,11 @@ import { actions, kea, key, path, props, propsChanged, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import React from 'react' + import { query } from '~/queries/query' +import { HogQLMetadata, HogQLMetadataResponse, NodeKind } from '~/queries/schema' import type { hogQLEditorLogicType } from './hogQLEditorLogicType' -import { HogQLMetadata, HogQLMetadataResponse, NodeKind } from '~/queries/schema' -import { loaders } from 'kea-loaders' -import React from 'react' export interface HogQLEditorLogicProps { key: string diff --git a/frontend/src/lib/components/InsightLabel/index.tsx b/frontend/src/lib/components/InsightLabel/index.tsx index 2f11144d7c2cf..82adff76f6f38 100644 --- a/frontend/src/lib/components/InsightLabel/index.tsx +++ b/frontend/src/lib/components/InsightLabel/index.tsx @@ -1,15 +1,17 @@ -import { ActionFilter, BreakdownKeyType } from '~/types' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { capitalizeFirstLetter, hexToRGBA, midEllipsis } from 'lib/utils' import './InsightLabel.scss' -import { SeriesLetter } from 'lib/components/SeriesGlyph' -import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' + +import { LemonTag } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useValues } from 'kea' +import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter, hexToRGBA, midEllipsis } from 'lib/utils' import { mathsLogic } from 'scenes/trends/mathsLogic' -import clsx from 'clsx' + import { groupsModel } from '~/models/groupsModel' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { LemonTag } from '@posthog/lemon-ui' +import { ActionFilter, BreakdownKeyType } from '~/types' export enum IconSize { Small = 'small', diff --git a/frontend/src/lib/components/InsightLegend/InsightLegend.tsx b/frontend/src/lib/components/InsightLegend/InsightLegend.tsx index ef82a062c8b95..30f1db7ac0245 100644 --- a/frontend/src/lib/components/InsightLegend/InsightLegend.tsx +++ b/frontend/src/lib/components/InsightLegend/InsightLegend.tsx @@ -1,10 +1,12 @@ import './InsightLegend.scss' + +import clsx from 'clsx' import { useActions, useValues } from 'kea' import { insightLogic } from 'scenes/insights/insightLogic' -import clsx from 'clsx' +import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' + import { InsightLegendRow } from './InsightLegendRow' import { shouldHighlightThisRow } from './utils' -import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' export interface InsightLegendProps { readOnly?: boolean diff --git a/frontend/src/lib/components/InsightLegend/InsightLegendRow.tsx b/frontend/src/lib/components/InsightLegend/InsightLegendRow.tsx index 900dec1969031..cf20e9d60d5e5 100644 --- a/frontend/src/lib/components/InsightLegend/InsightLegendRow.tsx +++ b/frontend/src/lib/components/InsightLegend/InsightLegendRow.tsx @@ -1,12 +1,13 @@ -import { InsightLabel } from 'lib/components/InsightLabel' import { getSeriesColor } from 'lib/colors' +import { InsightLabel } from 'lib/components/InsightLabel' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { formatCompareLabel } from 'scenes/insights/views/InsightsTable/columns/SeriesColumn' -import { ChartDisplayType } from '~/types' +import { useEffect, useRef } from 'react' import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' +import { formatCompareLabel } from 'scenes/insights/views/InsightsTable/columns/SeriesColumn' import { IndexedTrendResult } from 'scenes/trends/types' -import { useEffect, useRef } from 'react' + import { TrendsFilter } from '~/queries/schema' +import { ChartDisplayType } from '~/types' type InsightLegendRowProps = { hiddenLegendKeys: Record diff --git a/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx b/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx index b8a95dc839191..1dddc064cb281 100644 --- a/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx +++ b/frontend/src/lib/components/IntervalFilter/IntervalFilter.tsx @@ -1,7 +1,8 @@ +import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonSelect } from '@posthog/lemon-ui' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' + import { InsightQueryNode } from '~/queries/schema' interface IntervalFilterProps { diff --git a/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts b/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts index d2136d8d8a682..e864d54edfc39 100644 --- a/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts +++ b/frontend/src/lib/components/IntervalFilter/intervalFilterLogic.ts @@ -1,14 +1,16 @@ -import { kea, props, key, path, connect, actions, reducers, listeners } from 'kea' -import { objectsEqual, dateMapping } from 'lib/utils' -import type { intervalFilterLogicType } from './intervalFilterLogicType' +import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea' import { IntervalKeyType, Intervals, intervals } from 'lib/components/IntervalFilter/intervals' -import { BaseMathType, InsightLogicProps, IntervalType } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { dayjs } from 'lib/dayjs' -import { InsightQueryNode, TrendsQuery } from '~/queries/schema' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' +import { dateMapping, objectsEqual } from 'lib/utils' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' + +import { InsightQueryNode, TrendsQuery } from '~/queries/schema' +import { BaseMathType, InsightLogicProps, IntervalType } from '~/types' + +import type { intervalFilterLogicType } from './intervalFilterLogicType' export const intervalFilterLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/lib/components/JSBookmarklet.tsx b/frontend/src/lib/components/JSBookmarklet.tsx index 54d87d84cc9af..c0cee95aed608 100644 --- a/frontend/src/lib/components/JSBookmarklet.tsx +++ b/frontend/src/lib/components/JSBookmarklet.tsx @@ -1,9 +1,10 @@ -import { TeamBasicType } from '~/types' import { useActions } from 'kea' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { IconBookmarkBorder } from 'lib/lemon-ui/icons' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { useEffect, useRef } from 'react' +import { TeamBasicType } from '~/types' + export function JSBookmarklet({ team }: { team: TeamBasicType }): JSX.Element { const initCall = `posthog.init('${team?.api_token}',{api_host:'${location.origin}', loaded: () => alert('PostHog is now tracking events!')})` const href = `javascript:(function()%7Bif%20(window.posthog)%20%7Balert(%22Error%3A%20PostHog%20already%20is%20installed%20on%20this%20site%22)%7D%20else%20%7B!function(t%2Ce)%7Bvar%20o%2Cn%2Cp%2Cr%3Be.__SV%7C%7C(window.posthog%3De%2Ce._i%3D%5B%5D%2Ce.init%3Dfunction(i%2Cs%2Ca)%7Bfunction%20g(t%2Ce)%7Bvar%20o%3De.split(%22.%22)%3B2%3D%3Do.length%26%26(t%3Dt%5Bo%5B0%5D%5D%2Ce%3Do%5B1%5D)%2Ct%5Be%5D%3Dfunction()%7Bt.push(%5Be%5D.concat(Array.prototype.slice.call(arguments%2C0)))%7D%7D(p%3Dt.createElement(%22script%22)).type%3D%22text%2Fjavascript%22%2Cp.async%3D!0%2Cp.src%3Ds.api_host%2B%22%2Fstatic%2Farray.js%22%2C(r%3Dt.getElementsByTagName(%22script%22)%5B0%5D).parentNode.insertBefore(p%2Cr)%3Bvar%20u%3De%3Bfor(void%200!%3D%3Da%3Fu%3De%5Ba%5D%3D%5B%5D%3Aa%3D%22posthog%22%2Cu.people%3Du.people%7C%7C%5B%5D%2Cu.toString%3Dfunction(t)%7Bvar%20e%3D%22posthog%22%3Breturn%22posthog%22!%3D%3Da%26%26(e%2B%3D%22.%22%2Ba)%2Ct%7C%7C(e%2B%3D%22%20(stub)%22)%2Ce%7D%2Cu.people.toString%3Dfunction()%7Breturn%20u.toString(1)%2B%22.people%20(stub)%22%7D%2Co%3D%22capture%20identify%20alias%20people.set%20people.set_once%20set_config%20register%20register_once%20unregister%20opt_out_capturing%20has_opted_out_capturing%20opt_in_capturing%20reset%20isFeatureEnabled%20onFeatureFlags%22.split(%22%20%22)%2Cn%3D0%3Bn%3Co.length%3Bn%2B%2B)g(u%2Co%5Bn%5D)%3Be._i.push(%5Bi%2Cs%2Ca%5D)%7D%2Ce.__SV%3D1)%7D(document%2Cwindow.posthog%7C%7C%5B%5D)%3B${encodeURIComponent( diff --git a/frontend/src/lib/components/JSSnippet.tsx b/frontend/src/lib/components/JSSnippet.tsx index 8458e79ad9c1b..1f0751187ef7e 100644 --- a/frontend/src/lib/components/JSSnippet.tsx +++ b/frontend/src/lib/components/JSSnippet.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' export function JSSnippet(): JSX.Element { diff --git a/frontend/src/lib/components/Map/Map.stories.tsx b/frontend/src/lib/components/Map/Map.stories.tsx index e7e120a8244a3..0fd7feb8fad7e 100644 --- a/frontend/src/lib/components/Map/Map.stories.tsx +++ b/frontend/src/lib/components/Map/Map.stories.tsx @@ -7,18 +7,13 @@ const coordinates: [number, number] = [0.119167, 52.205276] const meta: Meta = { title: 'Components/Map', component: Map, - tags: ['autodocs'], + tags: ['autodocs', 'test-skip'], // :TRICKY: We can't use markers in Storybook stories, as the Marker class is // not JSON-serializable (circular structure). args: { center: coordinates, className: 'h-60', }, - parameters: { - testOptions: { - skip: true, - }, - }, } type Story = StoryObj diff --git a/frontend/src/lib/components/Map/Map.tsx b/frontend/src/lib/components/Map/Map.tsx index a7365d53f2773..53686f8da2f18 100644 --- a/frontend/src/lib/components/Map/Map.tsx +++ b/frontend/src/lib/components/Map/Map.tsx @@ -1,15 +1,15 @@ -import { useEffect, useRef } from 'react' +import 'maplibre-gl/dist/maplibre-gl.css' +import './Maplibre.scss' + import { useValues } from 'kea' import maplibregl, { Map as RawMap, Marker } from 'maplibre-gl' import { Protocol } from 'pmtiles' import layers from 'protomaps-themes-base' +import { useEffect, useRef } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import useResizeObserver from 'use-resize-observer' -import 'maplibre-gl/dist/maplibre-gl.css' -import './Maplibre.scss' - import { themeLogic } from '~/layout/navigation-3000/themeLogic' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' const protocol = new Protocol() maplibregl.addProtocol('pmtiles', protocol.tile) diff --git a/frontend/src/lib/components/NotFound/NotFound.stories.tsx b/frontend/src/lib/components/NotFound/NotFound.stories.tsx index 948b590749404..0450fd868427f 100644 --- a/frontend/src/lib/components/NotFound/NotFound.stories.tsx +++ b/frontend/src/lib/components/NotFound/NotFound.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn, Meta, StoryObj } from '@storybook/react' +import { Meta, StoryFn, StoryObj } from '@storybook/react' import { NotFound } from './index' diff --git a/frontend/src/lib/components/NotFound/index.tsx b/frontend/src/lib/components/NotFound/index.tsx index 9e18b27d6ecc9..f9539f64c0185 100644 --- a/frontend/src/lib/components/NotFound/index.tsx +++ b/frontend/src/lib/components/NotFound/index.tsx @@ -1,11 +1,13 @@ -import { capitalizeFirstLetter } from 'lib/utils' -import { Link } from 'lib/lemon-ui/Link' import './NotFound.scss' + +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { supportLogic } from '../Support/supportLogic' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { Link } from 'lib/lemon-ui/Link' +import { capitalizeFirstLetter } from 'lib/utils' import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' -import { LemonButton } from '@posthog/lemon-ui' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { supportLogic } from '../Support/supportLogic' interface NotFoundProps { object: string // Type of object that was not found (e.g. `dashboard`, `insight`, `action`, ...) diff --git a/frontend/src/lib/components/ObjectTags/ObjectTags.stories.tsx b/frontend/src/lib/components/ObjectTags/ObjectTags.stories.tsx index d8df145c5094e..4acbe9e6c3909 100644 --- a/frontend/src/lib/components/ObjectTags/ObjectTags.stories.tsx +++ b/frontend/src/lib/components/ObjectTags/ObjectTags.stories.tsx @@ -1,4 +1,5 @@ -import { StoryFn, Meta, StoryObj } from '@storybook/react' +import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { ObjectTags, ObjectTagsProps } from './ObjectTags' type Story = StoryObj diff --git a/frontend/src/lib/components/ObjectTags/ObjectTags.tsx b/frontend/src/lib/components/ObjectTags/ObjectTags.tsx index 00d447b71ff0c..f0bcb900d0a6e 100644 --- a/frontend/src/lib/components/ObjectTags/ObjectTags.tsx +++ b/frontend/src/lib/components/ObjectTags/ObjectTags.tsx @@ -1,16 +1,18 @@ -import { Tag, Select } from 'antd' -import { colorForString } from 'lib/utils' -import { CSSProperties, useMemo } from 'react' // eslint-disable-next-line no-restricted-imports -import { SyncOutlined, CloseOutlined } from '@ant-design/icons' -import { SelectGradientOverflow } from '../SelectGradientOverflow' +import { CloseOutlined, SyncOutlined } from '@ant-design/icons' +import { IconPlus } from '@posthog/icons' +import { Select, Tag } from 'antd' +import clsx from 'clsx' import { useActions, useValues } from 'kea' import { objectTagsLogic } from 'lib/components/ObjectTags/objectTagsLogic' -import { AvailableFeature } from '~/types' -import { sceneLogic } from 'scenes/sceneLogic' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import clsx from 'clsx' -import { IconPlus } from '@posthog/icons' +import { colorForString } from 'lib/utils' +import { CSSProperties, useMemo } from 'react' +import { sceneLogic } from 'scenes/sceneLogic' + +import { AvailableFeature } from '~/types' + +import { SelectGradientOverflow } from '../SelectGradientOverflow' interface ObjectTagsPropsBase { tags: string[] diff --git a/frontend/src/lib/components/ObjectTags/objectTagsLogic.test.ts b/frontend/src/lib/components/ObjectTags/objectTagsLogic.test.ts index de0716d7bfbfa..4564a203301c0 100644 --- a/frontend/src/lib/components/ObjectTags/objectTagsLogic.test.ts +++ b/frontend/src/lib/components/ObjectTags/objectTagsLogic.test.ts @@ -1,7 +1,8 @@ -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' import { objectTagsLogic, ObjectTagsLogicProps } from 'lib/components/ObjectTags/objectTagsLogic' +import { initKeaTests } from '~/test/init' + describe('objectTagsLogic', () => { let logic: ReturnType let props: ObjectTagsLogicProps diff --git a/frontend/src/lib/components/ObjectTags/objectTagsLogic.ts b/frontend/src/lib/components/ObjectTags/objectTagsLogic.ts index f1dbffcf57473..3770b27a8a491 100644 --- a/frontend/src/lib/components/ObjectTags/objectTagsLogic.ts +++ b/frontend/src/lib/components/ObjectTags/objectTagsLogic.ts @@ -1,7 +1,8 @@ +import equal from 'fast-deep-equal' import { actions, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' -import type { objectTagsLogicType } from './objectTagsLogicType' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import equal from 'fast-deep-equal' + +import type { objectTagsLogicType } from './objectTagsLogicType' export interface ObjectTagsLogicProps { id: number diff --git a/frontend/src/lib/components/PageHeader.tsx b/frontend/src/lib/components/PageHeader.tsx index 5aee5b5d0bc01..365b00def6c2a 100644 --- a/frontend/src/lib/components/PageHeader.tsx +++ b/frontend/src/lib/components/PageHeader.tsx @@ -1,9 +1,11 @@ import clsx from 'clsx' import { useValues } from 'kea' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { Within3000PageHeaderContext } from 'lib/lemon-ui/LemonButton/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { createPortal } from 'react-dom' import { DraggableToNotebook, DraggableToNotebookProps } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' + import { breadcrumbsLogic } from '~/layout/navigation/Breadcrumbs/breadcrumbsLogic' interface PageHeaderProps { @@ -32,7 +34,6 @@ export function PageHeader({ return ( <> - {} {(!is3000 || description) && (
    @@ -49,10 +50,18 @@ export function PageHeader({ {!is3000 &&
    {buttons}
    }
    )} - {is3000 && buttons && actionsContainer && createPortal(buttons, actionsContainer)} + {is3000 && + buttons && + actionsContainer && + createPortal( + + {buttons} + , + actionsContainer + )} {caption &&
    {caption}
    } - {delimited && } + {delimited && } ) } diff --git a/frontend/src/lib/components/PasswordStrength.tsx b/frontend/src/lib/components/PasswordStrength.tsx index 30e373dbaba51..f3f5be49ef656 100644 --- a/frontend/src/lib/components/PasswordStrength.tsx +++ b/frontend/src/lib/components/PasswordStrength.tsx @@ -1,6 +1,6 @@ import { Progress } from 'antd' -import zxcvbn from 'zxcvbn' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import zxcvbn from 'zxcvbn' export default function PasswordStrength({ password = '', diff --git a/frontend/src/lib/components/PathCleanFilters/PathCleanFilterAddItemButton.tsx b/frontend/src/lib/components/PathCleanFilters/PathCleanFilterAddItemButton.tsx index cd01609e82f53..91317e1d559ea 100644 --- a/frontend/src/lib/components/PathCleanFilters/PathCleanFilterAddItemButton.tsx +++ b/frontend/src/lib/components/PathCleanFilters/PathCleanFilterAddItemButton.tsx @@ -1,9 +1,9 @@ +import { IconPlus } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Popover } from 'lib/lemon-ui/Popover/Popover' import { useState } from 'react' import { PathCleaningFilter } from '~/types' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconPlus } from 'lib/lemon-ui/icons' import { PathRegexPopover } from './PathRegexPopover' diff --git a/frontend/src/lib/components/PathCleanFilters/PathCleanFilterItem.tsx b/frontend/src/lib/components/PathCleanFilters/PathCleanFilterItem.tsx index e4d73e9fd6f6b..588890a85b426 100644 --- a/frontend/src/lib/components/PathCleanFilters/PathCleanFilterItem.tsx +++ b/frontend/src/lib/components/PathCleanFilters/PathCleanFilterItem.tsx @@ -1,9 +1,9 @@ -import { useState } from 'react' - -import { PathCleaningFilter } from '~/types' import { LemonSnack } from '@posthog/lemon-ui' import { Popover } from 'lib/lemon-ui/Popover/Popover' import { midEllipsis } from 'lib/utils' +import { useState } from 'react' + +import { PathCleaningFilter } from '~/types' import { PathRegexPopover } from './PathRegexPopover' diff --git a/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.stories.tsx b/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.stories.tsx index 02ab50eb82b24..de1b7320c6290 100644 --- a/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.stories.tsx +++ b/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useState } from 'react' + import { PathCleaningFilter } from '~/types' import { PathCleanFilters, PathCleanFiltersProps } from './PathCleanFilters' diff --git a/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.tsx b/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.tsx index fb2bd5ef60895..01b26636feef9 100644 --- a/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.tsx +++ b/frontend/src/lib/components/PathCleanFilters/PathCleanFilters.tsx @@ -1,6 +1,7 @@ import { PathCleaningFilter } from '~/types' -import { PathCleanFilterItem } from './PathCleanFilterItem' + import { PathCleanFilterAddItemButton } from './PathCleanFilterAddItemButton' +import { PathCleanFilterItem } from './PathCleanFilterItem' export interface PathCleanFiltersProps { filters?: PathCleaningFilter[] diff --git a/frontend/src/lib/components/PathCleanFilters/PathRegexPopover.tsx b/frontend/src/lib/components/PathCleanFilters/PathRegexPopover.tsx index e1a97e01f391b..70720f11a30cb 100644 --- a/frontend/src/lib/components/PathCleanFilters/PathRegexPopover.tsx +++ b/frontend/src/lib/components/PathCleanFilters/PathRegexPopover.tsx @@ -1,6 +1,6 @@ +import { LemonButton, LemonDivider, LemonInput } from '@posthog/lemon-ui' import { useState } from 'react' -import { LemonInput, LemonButton, LemonDivider } from '@posthog/lemon-ui' import { PathCleaningFilter } from '~/types' interface PathRegexPopoverProps { diff --git a/frontend/src/lib/components/PayGateMini/PayGateMini.tsx b/frontend/src/lib/components/PayGateMini/PayGateMini.tsx index 46e5f491da484..f801f600cf045 100644 --- a/frontend/src/lib/components/PayGateMini/PayGateMini.tsx +++ b/frontend/src/lib/components/PayGateMini/PayGateMini.tsx @@ -1,13 +1,15 @@ +import './PayGateMini.scss' + +import { Link } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { AvailableFeature } from '~/types' -import { userLogic } from 'scenes/userLogic' +import { FEATURE_MINIMUM_PLAN, POSTHOG_CLOUD_STANDARD_PLAN } from 'lib/constants' import { IconEmojiPeople, IconLightBulb, IconLock, IconPremium } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import './PayGateMini.scss' -import { FEATURE_MINIMUM_PLAN, POSTHOG_CLOUD_STANDARD_PLAN } from 'lib/constants' -import clsx from 'clsx' -import { Link } from '@posthog/lemon-ui' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature } from '~/types' type PayGateSupportedFeatures = | AvailableFeature.DASHBOARD_PERMISSIONING diff --git a/frontend/src/lib/components/PayGatePage/PayGatePage.tsx b/frontend/src/lib/components/PayGatePage/PayGatePage.tsx index bc33e212df053..c579d564cbe1a 100644 --- a/frontend/src/lib/components/PayGatePage/PayGatePage.tsx +++ b/frontend/src/lib/components/PayGatePage/PayGatePage.tsx @@ -1,11 +1,13 @@ +import './PayGatePage.scss' + import { useValues } from 'kea' -import { identifierToHuman } from 'lib/utils' import { IconOpenInNew } from 'lib/lemon-ui/icons' -import './PayGatePage.scss' -import { AvailableFeature } from '~/types' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { identifierToHuman } from 'lib/utils' import { billingLogic } from 'scenes/billing/billingLogic' +import { AvailableFeature } from '~/types' + interface PayGatePageInterface { header: string | JSX.Element caption: string | JSX.Element diff --git a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx b/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx index c65b8df3f901c..868c62de41edd 100644 --- a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx +++ b/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.stories.tsx @@ -1,6 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useState } from 'react' + import { mswDecorator } from '~/mocks/browser' + import { PersonPropertySelect, PersonPropertySelectProps } from './PersonPropertySelect' type Story = StoryObj diff --git a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx b/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx index d14d233f90f3b..55afbbc8fc898 100644 --- a/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx +++ b/frontend/src/lib/components/PersonPropertySelect/PersonPropertySelect.tsx @@ -1,17 +1,16 @@ -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' -import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { closestCenter, DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core' +import { restrictToHorizontalAxis, restrictToParentElement } from '@dnd-kit/modifiers' +import { horizontalListSortingStrategy, SortableContext, useSortable } from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' import { LemonButton } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { IconPlus } from 'lib/lemon-ui/icons' import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' -import clsx from 'clsx' +import { Popover } from 'lib/lemon-ui/Popover/Popover' import { useState } from 'react' -import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from '@dnd-kit/core' -import { useSortable, SortableContext, horizontalListSortingStrategy } from '@dnd-kit/sortable' -import { CSS } from '@dnd-kit/utilities' -import { restrictToHorizontalAxis, restrictToParentElement } from '@dnd-kit/modifiers' - export interface PersonPropertySelectProps { addText: string onChange: (names: string[]) => void diff --git a/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.stories.tsx b/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.stories.tsx index 5c8e18d6c063f..659e1a1bc4d9e 100644 --- a/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.stories.tsx +++ b/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.stories.tsx @@ -1,7 +1,9 @@ import { Meta } from '@storybook/react' -import { ProductIntroduction } from './ProductIntroduction' + import { ProductKey } from '~/types' +import { ProductIntroduction } from './ProductIntroduction' + const meta: Meta = { title: 'Components/Product Empty State', component: ProductIntroduction, diff --git a/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.tsx b/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.tsx index 0a0917a29280e..02e59aeca94db 100644 --- a/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.tsx +++ b/frontend/src/lib/components/ProductIntroduction/ProductIntroduction.tsx @@ -1,10 +1,12 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useActions } from 'kea' import { IconClose, IconOpenInNew, IconPlus } from 'lib/lemon-ui/icons' -import { BuilderHog3, DetectiveHog } from '../hedgehogs' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { userLogic } from 'scenes/userLogic' -import { useActions } from 'kea' + import { ProductKey } from '~/types' +import { BuilderHog3, DetectiveHog } from '../hedgehogs' + export const ProductIntroduction = ({ productName, productKey, diff --git a/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx b/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx index 4a577e8c10741..732ff6542b17b 100644 --- a/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx +++ b/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx @@ -1,7 +1,9 @@ import { Meta, StoryFn } from '@storybook/react' -import { PropertiesTable as PropertiesTableComponent } from '.' + import { PropertyDefinitionType } from '~/types' +import { PropertiesTable as PropertiesTableComponent } from '.' + const meta: Meta = { title: 'Components/Properties Table', component: PropertiesTableComponent, diff --git a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx index f96fb045c022f..eb327e49bf990 100644 --- a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx +++ b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx @@ -1,21 +1,23 @@ -import { useMemo, useState } from 'react' +import './PropertiesTable.scss' -import { KEY_MAPPING, keyMappingKeys } from 'lib/taxonomy' -import { PropertyKeyInfo } from '../PropertyKeyInfo' +import { IconPencil } from '@posthog/icons' +import { LemonCheckbox, LemonInput, Link } from '@posthog/lemon-ui' import { Dropdown, Input, Menu, Popconfirm } from 'antd' -import { isURL } from 'lib/utils' -import { IconDeleteForever } from 'lib/lemon-ui/icons' -import './PropertiesTable.scss' -import { LemonTable, LemonTableColumns, LemonTableProps } from 'lib/lemon-ui/LemonTable' -import { CopyToClipboardInline } from '../CopyToClipboard' +import clsx from 'clsx' import { useValues } from 'kea' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { IconDeleteForever } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonTable, LemonTableColumns, LemonTableProps } from 'lib/lemon-ui/LemonTable' +import { KEY_MAPPING, keyMappingKeys } from 'lib/taxonomy' +import { isURL } from 'lib/utils' +import { useMemo, useState } from 'react' import { NewProperty } from 'scenes/persons/NewProperty' -import { LemonCheckbox, LemonInput, Link } from '@posthog/lemon-ui' -import clsx from 'clsx' + +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' import { PropertyDefinitionType } from '~/types' -import { IconPencil } from '@posthog/icons' + +import { CopyToClipboardInline } from '../CopyToClipboard' +import { PropertyKeyInfo } from '../PropertyKeyInfo' type HandledType = 'string' | 'number' | 'bigint' | 'boolean' | 'undefined' | 'null' type Type = HandledType | 'symbol' | 'object' | 'function' diff --git a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx index 330e7b5597229..3d38ab578827d 100644 --- a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx +++ b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.stories.tsx @@ -1,7 +1,9 @@ import { Meta } from '@storybook/react' import { MOCK_TEAM_ID } from 'lib/api.mock' + import { useStorybookMocks } from '~/mocks/browser' import { ChartDisplayType, PersonActorType } from '~/types' + import { PropertiesTimeline } from '.' import { RawPropertiesTimelineResult } from './propertiesTimelineLogic' diff --git a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.tsx b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.tsx index ae7e811ce0608..08549fb345aef 100644 --- a/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.tsx +++ b/frontend/src/lib/components/PropertiesTimeline/PropertiesTimeline.tsx @@ -1,15 +1,17 @@ +import { LemonDivider } from '@posthog/lemon-ui' import { Properties } from '@posthog/plugin-scaffold' -import { PropertiesTable } from 'lib/components/PropertiesTable' import { useActions, useValues } from 'kea' -import { LemonDivider } from '@posthog/lemon-ui' -import { propertiesTimelineLogic, PropertiesTimelineProps } from './propertiesTimelineLogic' -import { TimelineSeekbar } from '../TimelineSeekbar' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { PropertiesTable } from 'lib/components/PropertiesTable' import { IconInfo } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { humanList } from 'lib/utils' import { teamLogic } from 'scenes/teamLogic' + import { PropertyDefinitionType } from '~/types' +import { TimelineSeekbar } from '../TimelineSeekbar' +import { propertiesTimelineLogic, PropertiesTimelineProps } from './propertiesTimelineLogic' + export function PropertiesTimeline({ actor, filter }: PropertiesTimelineProps): JSX.Element { const logic = propertiesTimelineLogic({ actor, filter }) const { points, crucialPropertyKeys, dateRange, resultLoading, selectedPointIndex } = useValues(logic) diff --git a/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts b/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts index 7828340d23ae7..adff5170c3cdf 100644 --- a/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts +++ b/frontend/src/lib/components/PropertiesTimeline/propertiesTimelineLogic.ts @@ -1,14 +1,15 @@ import { Properties } from '@posthog/plugin-scaffold' +import { captureException } from '@sentry/react' +import { actions, afterMount, connect, kea, key, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { Dayjs, dayjsUtcToTimezone } from 'lib/dayjs' +import { apiGetWithTimeToSeeDataTracking } from 'lib/internalMetrics' import { toParams, uuid } from 'lib/utils' +import { teamLogic } from 'scenes/teamLogic' + import { ActorType, PropertiesTimelineFilterType } from '~/types' -import { kea, key, props, path, connect, afterMount, selectors, reducers, actions } from 'kea' -import { loaders } from 'kea-loaders' import type { propertiesTimelineLogicType } from './propertiesTimelineLogicType' -import { teamLogic } from 'scenes/teamLogic' -import { apiGetWithTimeToSeeDataTracking } from 'lib/internalMetrics' -import { captureException } from '@sentry/react' export interface PropertiesTimelinePoint { timestamp: Dayjs diff --git a/frontend/src/lib/components/PropertyFilters/PathItemFilters.tsx b/frontend/src/lib/components/PropertyFilters/PathItemFilters.tsx index a79aa8c953e3d..207bb71599d5c 100644 --- a/frontend/src/lib/components/PropertyFilters/PathItemFilters.tsx +++ b/frontend/src/lib/components/PropertyFilters/PathItemFilters.tsx @@ -1,13 +1,15 @@ -import { CSSProperties, useEffect } from 'react' +import { LemonButton } from '@posthog/lemon-ui' import { BindLogic, useActions, useValues } from 'kea' -import { propertyFilterLogic } from './propertyFilterLogic' +import { IconPlusMini } from 'lib/lemon-ui/icons' +import { objectsEqual } from 'lib/utils' +import { CSSProperties, useEffect } from 'react' + import { AnyPropertyFilter, EmptyPropertyFilter, PropertyFilterType, PropertyOperator } from '~/types' + +import { SimpleOption, TaxonomicFilterGroupType } from '../TaxonomicFilter/types' import { PathItemSelector } from './components/PathItemSelector' import { PropertyFilterButton } from './components/PropertyFilterButton' -import { SimpleOption, TaxonomicFilterGroupType } from '../TaxonomicFilter/types' -import { objectsEqual } from 'lib/utils' -import { LemonButton } from '@posthog/lemon-ui' -import { IconPlusMini } from 'lib/lemon-ui/icons' +import { propertyFilterLogic } from './propertyFilterLogic' interface PropertyFiltersProps { endpoint?: string | null diff --git a/frontend/src/lib/components/PropertyFilters/PropertyFilters.stories.tsx b/frontend/src/lib/components/PropertyFilters/PropertyFilters.stories.tsx index 484b294b29b47..134b04d32b1f0 100644 --- a/frontend/src/lib/components/PropertyFilters/PropertyFilters.stories.tsx +++ b/frontend/src/lib/components/PropertyFilters/PropertyFilters.stories.tsx @@ -1,7 +1,9 @@ import { Meta } from '@storybook/react' +import PropertyFiltersDisplay from 'lib/components/PropertyFilters/components/PropertyFiltersDisplay' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' + +import { useStorybookMocks } from '~/mocks/browser' import { AnyPropertyFilter, PropertyOperator } from '~/types' -import PropertyFiltersDisplay from 'lib/components/PropertyFilters/components/PropertyFiltersDisplay' const meta: Meta = { title: 'Filters/PropertyFilters', @@ -31,6 +33,11 @@ const propertyFilters = [ ] as AnyPropertyFilter[] export function ComparingPropertyFilters(): JSX.Element { + useStorybookMocks({ + get: { + '/api/event/values/': [], + }, + }) return ( <>

    Pop-over enabled

    diff --git a/frontend/src/lib/components/PropertyFilters/PropertyFilters.tsx b/frontend/src/lib/components/PropertyFilters/PropertyFilters.tsx index dc9506368a0cd..24cf0b470087c 100644 --- a/frontend/src/lib/components/PropertyFilters/PropertyFilters.tsx +++ b/frontend/src/lib/components/PropertyFilters/PropertyFilters.tsx @@ -1,13 +1,16 @@ -import React, { useEffect } from 'react' -import { useValues, BindLogic, useActions } from 'kea' -import { propertyFilterLogic } from './propertyFilterLogic' -import { FilterRow } from './components/FilterRow' -import { AnyPropertyFilter, FilterLogicalOperator } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { TaxonomicPropertyFilter } from 'lib/components/PropertyFilters/components/TaxonomicPropertyFilter' import './PropertyFilters.scss' + +import { BindLogic, useActions, useValues } from 'kea' +import { TaxonomicPropertyFilter } from 'lib/components/PropertyFilters/components/TaxonomicPropertyFilter' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import React, { useEffect } from 'react' import { LogicalRowDivider } from 'scenes/cohorts/CohortFilters/CohortCriteriaRowBuilder' +import { AnyPropertyFilter, FilterLogicalOperator } from '~/types' + +import { FilterRow } from './components/FilterRow' +import { propertyFilterLogic } from './propertyFilterLogic' + interface PropertyFiltersProps { endpoint?: string | null propertyFilters?: AnyPropertyFilter[] | null diff --git a/frontend/src/lib/components/PropertyFilters/components/FilterRow.tsx b/frontend/src/lib/components/PropertyFilters/components/FilterRow.tsx index 4b8f10651e8d4..882af64fe0b7c 100644 --- a/frontend/src/lib/components/PropertyFilters/components/FilterRow.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/FilterRow.tsx @@ -1,13 +1,16 @@ -import React, { useState } from 'react' -import { AnyPropertyFilter, PathCleaningFilter } from '~/types' -import { PropertyFilterButton } from './PropertyFilterButton' -import { isValidPropertyFilter } from 'lib/components/PropertyFilters/utils' -import { Popover } from 'lib/lemon-ui/Popover/Popover' import './FilterRow.scss' + import clsx from 'clsx' +import { isValidPropertyFilter } from 'lib/components/PropertyFilters/utils' import { IconClose, IconDelete, IconPlus } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import React, { useState } from 'react' + +import { AnyPropertyFilter, PathCleaningFilter } from '~/types' + import { OperandTag } from './OperandTag' +import { PropertyFilterButton } from './PropertyFilterButton' interface FilterRowProps { item: Record diff --git a/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.stories.tsx b/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.stories.tsx index f3b0ef68557e7..545264a067bfe 100644 --- a/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.stories.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.stories.tsx @@ -3,6 +3,7 @@ import { OperatorValueSelect, OperatorValueSelectProps, } from 'lib/components/PropertyFilters/components/OperatorValueSelect' + import { PropertyDefinition, PropertyType } from '~/types' const meta: Meta = { diff --git a/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.tsx b/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.tsx index 817677a1f7f8a..5afaea8c4faeb 100644 --- a/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/OperatorValueSelect.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState } from 'react' -import { PropertyDefinition, PropertyFilterType, PropertyFilterValue, PropertyOperator, PropertyType } from '~/types' +import { LemonSelect, LemonSelectProps } from '@posthog/lemon-ui' +import { dayjs } from 'lib/dayjs' import { allOperatorsMapping, chooseOperatorMap, @@ -9,9 +9,11 @@ import { isOperatorRange, isOperatorRegex, } from 'lib/utils' +import { useEffect, useState } from 'react' + +import { PropertyDefinition, PropertyFilterType, PropertyFilterValue, PropertyOperator, PropertyType } from '~/types' + import { PropertyValue } from './PropertyValue' -import { dayjs } from 'lib/dayjs' -import { LemonSelect, LemonSelectProps } from '@posthog/lemon-ui' export interface OperatorValueSelectProps { type?: PropertyFilterType diff --git a/frontend/src/lib/components/PropertyFilters/components/PathItemSelector.tsx b/frontend/src/lib/components/PropertyFilters/components/PathItemSelector.tsx index 0efc10d4e9c90..8e23d445872e5 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PathItemSelector.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PathItemSelector.tsx @@ -1,7 +1,7 @@ -import { useState } from 'react' -import { Popover } from 'lib/lemon-ui/Popover/Popover' import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' import { SimpleOption, TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { useState } from 'react' interface PathItemSelectorProps { pathItem: TaxonomicFilterValue | undefined diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterButton.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterButton.tsx index f9fd9a37f7432..eea640e7c1b60 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterButton.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterButton.tsx @@ -1,14 +1,17 @@ import './PropertyFilterButton.scss' + import { Button } from 'antd' -import { AnyPropertyFilter } from '~/types' -import { CloseButton } from 'lib/components/CloseButton' -import { cohortsModel } from '~/models/cohortsModel' import { useValues } from 'kea' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { midEllipsis } from 'lib/utils' +import { CloseButton } from 'lib/components/CloseButton' +import { PropertyFilterIcon } from 'lib/components/PropertyFilters/components/PropertyFilterIcon' import { KEY_MAPPING } from 'lib/taxonomy' +import { midEllipsis } from 'lib/utils' import React from 'react' -import { PropertyFilterIcon } from 'lib/components/PropertyFilters/components/PropertyFilterIcon' + +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { AnyPropertyFilter } from '~/types' + import { formatPropertyLabel } from '../utils' export interface PropertyFilterButtonProps { diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterDatePicker.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterDatePicker.tsx index 2a2dd82c72c68..889e511a613e4 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterDatePicker.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterDatePicker.tsx @@ -1,10 +1,11 @@ +import { DatePicker } from 'lib/components/DatePicker' +import { PropertyValueProps } from 'lib/components/PropertyFilters/components/PropertyValue' import { dayjs } from 'lib/dayjs' -import { useEffect, useState } from 'react' -import { isOperatorDate } from 'lib/utils' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' +import { isOperatorDate } from 'lib/utils' +import { useEffect, useState } from 'react' + import { PropertyOperator } from '~/types' -import { PropertyValueProps } from 'lib/components/PropertyFilters/components/PropertyValue' -import { DatePicker } from 'lib/components/DatePicker' const dayJSMightParse = ( candidateDateTimeValue: string | number | (string | number)[] | null | undefined diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterIcon.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterIcon.tsx index 4b8cbeea830f0..fab60bffd85e2 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertyFilterIcon.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertyFilterIcon.tsx @@ -1,6 +1,7 @@ -import { PropertyFilterType } from '~/types' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { IconCohort, IconPerson, IconUnverifiedEvent } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' + +import { PropertyFilterType } from '~/types' export function PropertyFilterIcon({ type }: { type?: PropertyFilterType }): JSX.Element { let iconElement = <> diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertyFiltersDisplay.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertyFiltersDisplay.tsx index 1db0201362ea1..aef0dcb659a0a 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertyFiltersDisplay.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertyFiltersDisplay.tsx @@ -1,4 +1,5 @@ import { AnyPropertyFilter } from '~/types' + import { PropertyFilterButton } from './PropertyFilterButton' const PropertyFiltersDisplay = ({ filters }: { filters: AnyPropertyFilter[] }): JSX.Element => { diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertySelect.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertySelect.tsx index 3cc2e1b93a9e0..0ad81b779db76 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertySelect.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertySelect.tsx @@ -1,11 +1,12 @@ -import { useState } from 'react' -import Fuse from 'fuse.js' import { Select } from 'antd' +import Fuse from 'fuse.js' +import { useActions } from 'kea' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { SelectGradientOverflow } from 'lib/components/SelectGradientOverflow' -import { SelectOption } from '~/types' -import { useActions } from 'kea' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { useState } from 'react' + +import { SelectOption } from '~/types' interface Props { optionGroups: Array diff --git a/frontend/src/lib/components/PropertyFilters/components/PropertyValue.tsx b/frontend/src/lib/components/PropertyFilters/components/PropertyValue.tsx index 3d07a4bed1261..033e0b2680b1e 100644 --- a/frontend/src/lib/components/PropertyFilters/components/PropertyValue.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/PropertyValue.tsx @@ -1,15 +1,17 @@ -import { useEffect, useRef, useState } from 'react' +import './PropertyValue.scss' + import { AutoComplete } from 'antd' -import { isOperatorDate, isOperatorFlag, isOperatorMulti, toString } from 'lib/utils' -import { PropertyFilterType, PropertyOperator, PropertyType } from '~/types' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { PropertyFilterDatePicker } from 'lib/components/PropertyFilters/components/PropertyFilterDatePicker' import { DurationPicker } from 'lib/components/DurationPicker/DurationPicker' -import './PropertyValue.scss' -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' -import clsx from 'clsx' +import { PropertyFilterDatePicker } from 'lib/components/PropertyFilters/components/PropertyFilterDatePicker' import { propertyFilterTypeToPropertyDefinitionType } from 'lib/components/PropertyFilters/utils' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' +import { isOperatorDate, isOperatorFlag, isOperatorMulti, toString } from 'lib/utils' +import { useEffect, useRef, useState } from 'react' + +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { PropertyFilterType, PropertyOperator, PropertyType } from '~/types' export interface PropertyValueProps { propertyKey: string diff --git a/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx b/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx index 0c03aa12551b7..a3ff2475480fd 100644 --- a/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx @@ -1,30 +1,33 @@ import './TaxonomicPropertyFilter.scss' -import { useMemo } from 'react' + +import { LemonButtonWithDropdown } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useMountedLogic, useValues } from 'kea' +import { OperatorValueSelect } from 'lib/components/PropertyFilters/components/OperatorValueSelect' import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' -import { taxonomicPropertyFilterLogic } from './taxonomicPropertyFilterLogic' +import { PropertyFilterInternalProps } from 'lib/components/PropertyFilters/types' +import { + isGroupPropertyFilter, + isPropertyFilterWithOperator, + propertyFilterTypeToTaxonomicFilterType, +} from 'lib/components/PropertyFilters/utils' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { OperatorValueSelect } from 'lib/components/PropertyFilters/components/OperatorValueSelect' -import { isOperatorMulti, isOperatorRegex } from 'lib/utils' import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' import { TaxonomicFilterGroup, TaxonomicFilterGroupType, TaxonomicFilterValue, } from 'lib/components/TaxonomicFilter/types' -import { - isGroupPropertyFilter, - isPropertyFilterWithOperator, - propertyFilterTypeToTaxonomicFilterType, -} from 'lib/components/PropertyFilters/utils' -import { PropertyFilterInternalProps } from 'lib/components/PropertyFilters/types' -import clsx from 'clsx' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { AnyPropertyFilter, FilterLogicalOperator, PropertyDefinitionType, PropertyFilterType } from '~/types' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { LemonButtonWithDropdown } from '@posthog/lemon-ui' import { IconPlusMini } from 'lib/lemon-ui/icons' +import { isOperatorMulti, isOperatorRegex } from 'lib/utils' +import { useMemo } from 'react' + +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { AnyPropertyFilter, FilterLogicalOperator, PropertyDefinitionType, PropertyFilterType } from '~/types' + import { OperandTag } from './OperandTag' +import { taxonomicPropertyFilterLogic } from './taxonomicPropertyFilterLogic' let uniqueMemoizedIndex = 0 diff --git a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.test.ts b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.test.ts index d6ab1b8e0b53e..08bfe4962ba44 100644 --- a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.test.ts +++ b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.test.ts @@ -1,8 +1,9 @@ -import { initKeaTests } from '~/test/init' -import { taxonomicPropertyFilterLogic } from 'lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic' import { expectLogic } from 'kea-test-utils' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { taxonomicPropertyFilterLogic } from 'lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic' import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' + +import { initKeaTests } from '~/test/init' describe('the taxonomic property filter', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts index 6361dfb89489c..9ab0b1d638e77 100644 --- a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts +++ b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts @@ -1,21 +1,5 @@ -import { kea, props, key, path, connect, actions, reducers, selectors, listeners } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { TaxonomicPropertyFilterLogicProps } from 'lib/components/PropertyFilters/types' -import { - AnyPropertyFilter, - CohortPropertyFilter, - HogQLPropertyFilter, - PropertyDefinitionType, - PropertyFilterType, - PropertyOperator, - PropertyType, -} from '~/types' -import type { taxonomicPropertyFilterLogicType } from './taxonomicPropertyFilterLogicType' -import { cohortsModel } from '~/models/cohortsModel' -import { - TaxonomicFilterGroup, - TaxonomicFilterLogicProps, - TaxonomicFilterValue, -} from 'lib/components/TaxonomicFilter/types' import { isGroupPropertyFilter, isPropertyFilterWithOperator, @@ -25,7 +9,25 @@ import { taxonomicFilterTypeToPropertyFilterType, } from 'lib/components/PropertyFilters/utils' import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' +import { + TaxonomicFilterGroup, + TaxonomicFilterLogicProps, + TaxonomicFilterValue, +} from 'lib/components/TaxonomicFilter/types' + +import { cohortsModel } from '~/models/cohortsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { + AnyPropertyFilter, + CohortPropertyFilter, + HogQLPropertyFilter, + PropertyDefinitionType, + PropertyFilterType, + PropertyOperator, + PropertyType, +} from '~/types' + +import type { taxonomicPropertyFilterLogicType } from './taxonomicPropertyFilterLogicType' export const taxonomicPropertyFilterLogic = kea([ props({} as TaxonomicPropertyFilterLogicProps), diff --git a/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts b/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts index ad46588a1338e..39e8083a7b071 100644 --- a/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts +++ b/frontend/src/lib/components/PropertyFilters/propertyFilterLogic.ts @@ -1,9 +1,10 @@ import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { PropertyFilterLogicProps } from 'lib/components/PropertyFilters/types' +import { isValidPropertyFilter, parseProperties } from 'lib/components/PropertyFilters/utils' -import type { propertyFilterLogicType } from './propertyFilterLogicType' import { AnyPropertyFilter, EmptyPropertyFilter } from '~/types' -import { isValidPropertyFilter, parseProperties } from 'lib/components/PropertyFilters/utils' -import { PropertyFilterLogicProps } from 'lib/components/PropertyFilters/types' + +import type { propertyFilterLogicType } from './propertyFilterLogicType' export const propertyFilterLogic = kea([ path((key) => ['lib', 'components', 'PropertyFilters', 'propertyFilterLogic', key]), diff --git a/frontend/src/lib/components/PropertyFilters/types.ts b/frontend/src/lib/components/PropertyFilters/types.ts index 48af43e5573c7..0b7b671461f3e 100644 --- a/frontend/src/lib/components/PropertyFilters/types.ts +++ b/frontend/src/lib/components/PropertyFilters/types.ts @@ -1,11 +1,12 @@ -import { PropertyGroupFilter, AnyPropertyFilter, FilterLogicalOperator } from '~/types' +import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' +import { SelectGradientOverflowProps } from 'lib/components/SelectGradientOverflow' import { TaxonomicFilterGroup, TaxonomicFilterGroupType, TaxonomicFilterValue, } from 'lib/components/TaxonomicFilter/types' -import { SelectGradientOverflowProps } from 'lib/components/SelectGradientOverflow' -import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' + +import { AnyPropertyFilter, FilterLogicalOperator, PropertyGroupFilter } from '~/types' export interface PropertyFilterBaseProps { pageKey: string diff --git a/frontend/src/lib/components/PropertyFilters/utils.test.ts b/frontend/src/lib/components/PropertyFilters/utils.test.ts index 56ff189f94e0e..33ad74f8e35d6 100644 --- a/frontend/src/lib/components/PropertyFilters/utils.test.ts +++ b/frontend/src/lib/components/PropertyFilters/utils.test.ts @@ -1,3 +1,13 @@ +import { + breakdownFilterToTaxonomicFilterType, + convertPropertiesToPropertyGroup, + convertPropertyGroupToProperties, + isValidPropertyFilter, + propertyFilterTypeToTaxonomicFilterType, +} from 'lib/components/PropertyFilters/utils' + +import { BreakdownFilter } from '~/queries/schema' + import { AnyPropertyFilter, CohortPropertyFilter, @@ -9,15 +19,7 @@ import { PropertyOperator, SessionPropertyFilter, } from '../../../types' -import { - isValidPropertyFilter, - propertyFilterTypeToTaxonomicFilterType, - breakdownFilterToTaxonomicFilterType, - convertPropertiesToPropertyGroup, - convertPropertyGroupToProperties, -} from 'lib/components/PropertyFilters/utils' import { TaxonomicFilterGroupType } from '../TaxonomicFilter/types' -import { BreakdownFilter } from '~/queries/schema' describe('isValidPropertyFilter()', () => { it('returns values correctly', () => { diff --git a/frontend/src/lib/components/PropertyFilters/utils.ts b/frontend/src/lib/components/PropertyFilters/utils.ts index 6ce5fe2dd174f..e4bb8cbba1c7f 100644 --- a/frontend/src/lib/components/PropertyFilters/utils.ts +++ b/frontend/src/lib/components/PropertyFilters/utils.ts @@ -1,3 +1,8 @@ +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { allOperatorsMapping, isOperatorFlag } from 'lib/utils' + +import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' +import { BreakdownFilter } from '~/queries/schema' import { AnyFilterLike, AnyPropertyFilter, @@ -22,10 +27,6 @@ import { RecordingDurationFilter, SessionPropertyFilter, } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { allOperatorsMapping, isOperatorFlag } from 'lib/utils' -import { BreakdownFilter } from '~/queries/schema' -import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' export function isPropertyGroup( properties: diff --git a/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.stories.tsx b/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.stories.tsx index b7013d3b5dfb7..2a3896de8c889 100644 --- a/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.stories.tsx +++ b/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.stories.tsx @@ -1,10 +1,13 @@ -import { useState } from 'react' import { Meta } from '@storybook/react' -import { FilterLogicalOperator, FilterType, AnyPropertyFilter, PropertyGroupFilter, PropertyOperator } from '~/types' import { useMountedLogic } from 'kea' -import { PropertyGroupFilters } from './PropertyGroupFilters' -import { TaxonomicFilterGroupType } from '../TaxonomicFilter/types' +import { useState } from 'react' + +import { useStorybookMocks } from '~/mocks/browser' import { cohortsModel } from '~/models/cohortsModel' +import { AnyPropertyFilter, FilterLogicalOperator, FilterType, PropertyGroupFilter, PropertyOperator } from '~/types' + +import { TaxonomicFilterGroupType } from '../TaxonomicFilter/types' +import { PropertyGroupFilters } from './PropertyGroupFilters' const meta: Meta = { title: 'Filters/PropertyGroupFilters', @@ -36,6 +39,11 @@ const taxonomicGroupTypes = [ ] export function GroupPropertyFilters(): JSX.Element { + useStorybookMocks({ + get: { + '/api/event/values/': [], + }, + }) useMountedLogic(cohortsModel) const [propertyGroupFilter, setPropertyGroupFilter] = useState({ diff --git a/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.tsx b/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.tsx index 30d7c835ccf9f..6d532b1a948f0 100644 --- a/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.tsx +++ b/frontend/src/lib/components/PropertyGroupFilters/PropertyGroupFilters.tsx @@ -1,17 +1,20 @@ -import { useValues, BindLogic, useActions } from 'kea' -import { PropertyGroupFilter, PropertyGroupFilterValue, FilterType, AnyPropertyFilter } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import './PropertyGroupFilters.scss' -import { propertyGroupFilterLogic } from './propertyGroupFilterLogic' -import { PropertyFilters } from '../PropertyFilters/PropertyFilters' -import { GlobalFiltersTitle } from 'scenes/insights/common' + +import { BindLogic, useActions, useValues } from 'kea' +import { isPropertyGroupFilterLike } from 'lib/components/PropertyFilters/utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { IconCopy, IconDelete, IconPlusMini } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import React from 'react' -import { isPropertyGroupFilterLike } from 'lib/components/PropertyFilters/utils' +import { GlobalFiltersTitle } from 'scenes/insights/common' +import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' + import { AndOrFilterSelect } from '~/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect' +import { AnyPropertyFilter, FilterType, PropertyGroupFilter, PropertyGroupFilterValue } from '~/types' + +import { PropertyFilters } from '../PropertyFilters/PropertyFilters' +import { propertyGroupFilterLogic } from './propertyGroupFilterLogic' interface PropertyGroupFilters { value: PropertyGroupFilter diff --git a/frontend/src/lib/components/PropertyGroupFilters/propertyGroupFilterLogic.ts b/frontend/src/lib/components/PropertyGroupFilters/propertyGroupFilterLogic.ts index a592cf444e94e..cabfc14278390 100644 --- a/frontend/src/lib/components/PropertyGroupFilters/propertyGroupFilterLogic.ts +++ b/frontend/src/lib/components/PropertyGroupFilters/propertyGroupFilterLogic.ts @@ -1,12 +1,12 @@ import { actions, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' - -import { PropertyGroupFilter, FilterLogicalOperator, EmptyPropertyFilter } from '~/types' import { PropertyGroupFilterLogicProps } from 'lib/components/PropertyFilters/types' - -import type { propertyGroupFilterLogicType } from './propertyGroupFilterLogicType' import { objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + +import { EmptyPropertyFilter, FilterLogicalOperator, PropertyGroupFilter } from '~/types' + import { convertPropertiesToPropertyGroup } from '../PropertyFilters/utils' +import type { propertyGroupFilterLogicType } from './propertyGroupFilterLogicType' export const propertyGroupFilterLogic = kea([ path(['lib', 'components', 'PropertyGroupFilters', 'propertyGroupFilterLogic']), diff --git a/frontend/src/lib/components/PropertyIcon.stories.tsx b/frontend/src/lib/components/PropertyIcon.stories.tsx index 91b2149cb837d..229616979d58f 100644 --- a/frontend/src/lib/components/PropertyIcon.stories.tsx +++ b/frontend/src/lib/components/PropertyIcon.stories.tsx @@ -7,10 +7,7 @@ type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Icons/Property Icon', component: PropertyIcon, - parameters: { - testOptions: { skip: true }, // There are too many icons, the snapshots are huge in table form - }, - tags: ['autodocs'], + tags: ['autodocs', 'test-skip'], // There are too many icons, the snapshots are huge in table form } export default meta diff --git a/frontend/src/lib/components/PropertyIcon.tsx b/frontend/src/lib/components/PropertyIcon.tsx index 91f351b7f1093..d1a5bf8a6848d 100644 --- a/frontend/src/lib/components/PropertyIcon.tsx +++ b/frontend/src/lib/components/PropertyIcon.tsx @@ -1,3 +1,4 @@ +import clsx from 'clsx' import { IconAndroidOS, IconAppleIOS, @@ -17,10 +18,9 @@ import { IconWeb, IconWindows, } from 'lib/lemon-ui/icons' -import clsx from 'clsx' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { countryCodeToFlag } from 'scenes/insights/views/WorldMap' import { HTMLAttributes, ReactNode } from 'react' +import { countryCodeToFlag } from 'scenes/insights/views/WorldMap' export const PROPERTIES_ICON_MAP = { $browser: { diff --git a/frontend/src/lib/components/PropertyKeyInfo.stories.tsx b/frontend/src/lib/components/PropertyKeyInfo.stories.tsx index 715091e635075..632480f7f0f62 100644 --- a/frontend/src/lib/components/PropertyKeyInfo.stories.tsx +++ b/frontend/src/lib/components/PropertyKeyInfo.stories.tsx @@ -1,4 +1,4 @@ -import { StoryFn, Meta, StoryObj } from '@storybook/react' +import { Meta, StoryFn, StoryObj } from '@storybook/react' import { PropertyKeyInfo } from './PropertyKeyInfo' diff --git a/frontend/src/lib/components/PropertyKeyInfo.tsx b/frontend/src/lib/components/PropertyKeyInfo.tsx index 45aa9117086b7..07d2313b872a8 100644 --- a/frontend/src/lib/components/PropertyKeyInfo.tsx +++ b/frontend/src/lib/components/PropertyKeyInfo.tsx @@ -1,9 +1,10 @@ import './PropertyKeyInfo.scss' + +import { LemonDivider, TooltipProps } from '@posthog/lemon-ui' import clsx from 'clsx' import { Popover } from 'lib/lemon-ui/Popover' import { getKeyMapping, PropertyKey, PropertyType } from 'lib/taxonomy' import React, { useState } from 'react' -import { LemonDivider, TooltipProps } from '@posthog/lemon-ui' interface PropertyKeyInfoProps { value: PropertyKey diff --git a/frontend/src/lib/components/Resizer/Resizer.tsx b/frontend/src/lib/components/Resizer/Resizer.tsx index 7337be5e2a493..781592aae34bf 100644 --- a/frontend/src/lib/components/Resizer/Resizer.tsx +++ b/frontend/src/lib/components/Resizer/Resizer.tsx @@ -1,9 +1,11 @@ -import { useActions, useValues } from 'kea' import './Resizer.scss' -import { ResizerLogicProps, resizerLogic } from './resizerLogic' + import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { useEffect, useState } from 'react' +import { resizerLogic, ResizerLogicProps } from './resizerLogic' + export type ResizerProps = ResizerLogicProps & { offset?: number | string } diff --git a/frontend/src/lib/components/Resizer/resizerLogic.ts b/frontend/src/lib/components/Resizer/resizerLogic.ts index 4cf16cdf9fb5f..5d4992ac05586 100644 --- a/frontend/src/lib/components/Resizer/resizerLogic.ts +++ b/frontend/src/lib/components/Resizer/resizerLogic.ts @@ -1,7 +1,7 @@ import { actions, beforeUnmount, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import posthog from 'posthog-js' import type { resizerLogicType } from './resizerLogicType' -import posthog from 'posthog-js' export type ResizerEvent = { originX: number diff --git a/frontend/src/lib/components/RestrictedArea.tsx b/frontend/src/lib/components/RestrictedArea.tsx index a1ec4a419a06a..636ab88b06d08 100644 --- a/frontend/src/lib/components/RestrictedArea.tsx +++ b/frontend/src/lib/components/RestrictedArea.tsx @@ -1,10 +1,11 @@ import { useValues } from 'kea' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { useMemo } from 'react' + import { organizationLogic } from '../../scenes/organizationLogic' +import { isAuthenticatedTeam, teamLogic } from '../../scenes/teamLogic' import { EitherMembershipLevel, OrganizationMembershipLevel } from '../constants' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { membershipLevelToName } from '../utils/permissioning' -import { isAuthenticatedTeam, teamLogic } from '../../scenes/teamLogic' export interface RestrictedComponentProps { isRestricted: boolean diff --git a/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceModal.tsx b/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceModal.tsx index d74b7dd7abbca..e6e7aa1e9919f 100644 --- a/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceModal.tsx +++ b/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceModal.tsx @@ -1,16 +1,18 @@ +import { LemonDivider, LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { dashboardsModel } from '~/models/dashboardsModel' +import { SceneIcon } from 'lib/components/SceneDashboardChoice/SceneIcon' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' + +import { dashboardsModel } from '~/models/dashboardsModel' + import { sceneDashboardChoiceModalLogic, SceneDashboardChoiceModalProps, sceneDescription, } from './sceneDashboardChoiceModalLogic' -import { LemonRow } from 'lib/lemon-ui/LemonRow' -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { LemonDivider, LemonInput } from '@posthog/lemon-ui' -import { SceneIcon } from 'lib/components/SceneDashboardChoice/SceneIcon' export function SceneDashboardChoiceModal({ scene }: SceneDashboardChoiceModalProps): JSX.Element { const modalLogic = sceneDashboardChoiceModalLogic({ scene }) diff --git a/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired.tsx b/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired.tsx index efcc813b93cb8..2789d3b4f68b1 100644 --- a/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired.tsx +++ b/frontend/src/lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired.tsx @@ -1,10 +1,10 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { DashboardCompatibleScenes, sceneDescription, } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' -import { Scene } from 'scenes/sceneTypes' import { SceneIcon } from 'lib/components/SceneDashboardChoice/SceneIcon' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Scene } from 'scenes/sceneTypes' export function SceneDashboardChoiceRequired(props: { open: () => void diff --git a/frontend/src/lib/components/SceneDashboardChoice/SceneIcon.tsx b/frontend/src/lib/components/SceneDashboardChoice/SceneIcon.tsx index 50283fe1d4239..7cedf11ddd26c 100644 --- a/frontend/src/lib/components/SceneDashboardChoice/SceneIcon.tsx +++ b/frontend/src/lib/components/SceneDashboardChoice/SceneIcon.tsx @@ -1,7 +1,7 @@ +import clsx from 'clsx' import { DashboardCompatibleScenes } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' -import { Scene } from 'scenes/sceneTypes' import { IconCottage, IconPerson } from 'lib/lemon-ui/icons' -import clsx from 'clsx' +import { Scene } from 'scenes/sceneTypes' export function SceneIcon(props: { scene: DashboardCompatibleScenes; size: 'small' | 'large' }): JSX.Element | null { const className = clsx('text-warning', props.size === 'small' ? 'text-lg' : 'text-3xl') diff --git a/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.test.ts b/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.test.ts index 88182d3131a78..6e7df822c8b0a 100644 --- a/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.test.ts +++ b/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.test.ts @@ -1,11 +1,13 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { sceneDashboardChoiceModalLogic } from './sceneDashboardChoiceModalLogic' import { MOCK_DEFAULT_TEAM, MOCK_DEFAULT_USER } from 'lib/api.mock' +import { Scene } from 'scenes/sceneTypes' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' + import { useMocks } from '~/mocks/jest' -import { Scene } from 'scenes/sceneTypes' +import { initKeaTests } from '~/test/init' + +import { sceneDashboardChoiceModalLogic } from './sceneDashboardChoiceModalLogic' describe('sceneDashboardChoiceModalLogic', () => { let logic: ReturnType diff --git a/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.ts b/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.ts index 14925af1eae5d..171a80749d209 100644 --- a/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.ts +++ b/frontend/src/lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic.ts @@ -1,13 +1,14 @@ import Fuse from 'fuse.js' -import { actions, connect, kea, key, listeners, reducers, selectors, path, props } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { teamLogic } from 'scenes/teamLogic' -import { dashboardsModel } from '~/models/dashboardsModel' import { posthog } from 'posthog-js' import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { userLogic } from 'scenes/userLogic' + +import { dashboardsModel } from '~/models/dashboardsModel' import type { sceneDashboardChoiceModalLogicType } from './sceneDashboardChoiceModalLogicType' -import { userLogic } from 'scenes/userLogic' export type DashboardCompatibleScenes = Scene.ProjectHomepage | Scene.Person | Scene.Group diff --git a/frontend/src/lib/components/SelectGradientOverflow.tsx b/frontend/src/lib/components/SelectGradientOverflow.tsx index 1623c08976ae3..6a562bb85b59f 100644 --- a/frontend/src/lib/components/SelectGradientOverflow.tsx +++ b/frontend/src/lib/components/SelectGradientOverflow.tsx @@ -1,15 +1,18 @@ +import './SelectGradientOverflow.scss' + // eslint-disable-next-line no-restricted-imports import { LoadingOutlined } from '@ant-design/icons' -import { ReactElement, RefObject, useEffect, useRef, useState } from 'react' import { ConfigProvider, Empty, Select, Tag } from 'antd' import { RefSelectProps, SelectProps } from 'antd/lib/select' -import { CloseButton } from './CloseButton' -import { ANTD_TOOLTIP_PLACEMENTS, toString } from 'lib/utils' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import './SelectGradientOverflow.scss' import { useValues } from 'kea' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { ANTD_TOOLTIP_PLACEMENTS, toString } from 'lib/utils' +import { ReactElement, RefObject, useEffect, useRef, useState } from 'react' + import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { CloseButton } from './CloseButton' + interface DropdownGradientRendererProps { updateScrollGradient: () => void innerRef: RefObject diff --git a/frontend/src/lib/components/SeriesGlyph.tsx b/frontend/src/lib/components/SeriesGlyph.tsx index 024e3bde41b70..9e0cd0c2f5b6f 100644 --- a/frontend/src/lib/components/SeriesGlyph.tsx +++ b/frontend/src/lib/components/SeriesGlyph.tsx @@ -1,6 +1,7 @@ import { useValues } from 'kea' import { getSeriesColor } from 'lib/colors' import { alphabet, hexToRGBA, lightenDarkenColor, RGBToRGBA } from 'lib/utils' + import { themeLogic } from '~/layout/navigation-3000/themeLogic' interface SeriesGlyphProps { diff --git a/frontend/src/lib/components/Sharing/SharingModal.stories.tsx b/frontend/src/lib/components/Sharing/SharingModal.stories.tsx index 39d2e4b48ad06..2f5bd324ebc25 100644 --- a/frontend/src/lib/components/Sharing/SharingModal.stories.tsx +++ b/frontend/src/lib/components/Sharing/SharingModal.stories.tsx @@ -1,10 +1,12 @@ -import { useState } from 'react' import { Meta } from '@storybook/react' -import { SharingModal, SharingModalProps } from './SharingModal' -import { AvailableFeature, InsightModel, InsightShortId, InsightType } from '~/types' -import { useStorybookMocks } from '~/mocks/browser' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useState } from 'react' + +import { useStorybookMocks } from '~/mocks/browser' import { useAvailableFeatures } from '~/mocks/features' +import { AvailableFeature, InsightModel, InsightShortId, InsightType } from '~/types' + +import { SharingModal, SharingModalProps } from './SharingModal' const fakeInsight: Partial = { id: 123, diff --git a/frontend/src/lib/components/Sharing/SharingModal.tsx b/frontend/src/lib/components/Sharing/SharingModal.tsx index 0c61a40165bf0..587492080c504 100644 --- a/frontend/src/lib/components/Sharing/SharingModal.tsx +++ b/frontend/src/lib/components/Sharing/SharingModal.tsx @@ -1,21 +1,24 @@ -import { useEffect, useState } from 'react' -import { InsightModel, InsightShortId, InsightType } from '~/types' -import { useActions, useValues } from 'kea' -import { sharingLogic } from './sharingLogic' -import { LemonButton, LemonSwitch } from '@posthog/lemon-ui' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { IconGlobeLock, IconInfo, IconLink, IconLock, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { DashboardCollaboration } from 'scenes/dashboard/DashboardCollaborators' -import { Field } from 'lib/forms/Field' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import './SharingModal.scss' + +import { LemonButton, LemonSwitch } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { TitleWithIcon } from 'lib/components/TitleWithIcon' +import { Field } from 'lib/forms/Field' +import { IconGlobeLock, IconInfo, IconLink, IconLock, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { LemonModal } from 'lib/lemon-ui/LemonModal' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { useEffect, useState } from 'react' +import { DashboardCollaboration } from 'scenes/dashboard/DashboardCollaborators' + +import { InsightModel, InsightShortId, InsightType } from '~/types' + +import { sharingLogic } from './sharingLogic' export const SHARING_MODAL_WIDTH = 600 diff --git a/frontend/src/lib/components/Sharing/sharingLogic.ts b/frontend/src/lib/components/Sharing/sharingLogic.ts index d480fabeb8c7a..82898e841f40f 100644 --- a/frontend/src/lib/components/Sharing/sharingLogic.ts +++ b/frontend/src/lib/components/Sharing/sharingLogic.ts @@ -1,18 +1,18 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { AvailableFeature, InsightShortId, SharingConfigurationType } from '~/types' - -import api from 'lib/api' +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' +import api from 'lib/api' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { getInsightId } from 'scenes/insights/utils' - -import type { sharingLogicType } from './sharingLogicType' -import { ExportOptions } from '~/exporter/types' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { forms } from 'kea-forms' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + +import { ExportOptions } from '~/exporter/types' import { dashboardsModel } from '~/models/dashboardsModel' +import { AvailableFeature, InsightShortId, SharingConfigurationType } from '~/types' + +import type { sharingLogicType } from './sharingLogicType' export interface SharingLogicProps { dashboardId?: number diff --git a/frontend/src/lib/components/SmoothingFilter/SmoothingFilter.tsx b/frontend/src/lib/components/SmoothingFilter/SmoothingFilter.tsx index d21d6a9bb6808..ad286251e8f20 100644 --- a/frontend/src/lib/components/SmoothingFilter/SmoothingFilter.tsx +++ b/frontend/src/lib/components/SmoothingFilter/SmoothingFilter.tsx @@ -1,11 +1,12 @@ // eslint-disable-next-line no-restricted-imports import { FundOutlined } from '@ant-design/icons' -import { smoothingOptions } from './smoothings' +import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' -import { LemonSelect } from '@posthog/lemon-ui' + +import { smoothingOptions } from './smoothings' export function SmoothingFilter(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/lib/components/SocialLoginButton/SocialLoginButton.tsx b/frontend/src/lib/components/SocialLoginButton/SocialLoginButton.tsx index 04789b93cbb35..2f90e0ea44f63 100644 --- a/frontend/src/lib/components/SocialLoginButton/SocialLoginButton.tsx +++ b/frontend/src/lib/components/SocialLoginButton/SocialLoginButton.tsx @@ -1,12 +1,15 @@ -import { useValues } from 'kea' import clsx from 'clsx' -import { SocialLoginIcon } from './SocialLoginIcon' -import { SSOProvider } from '~/types' +import { useValues } from 'kea' +import { combineUrl, router } from 'kea-router' import { SSO_PROVIDER_NAMES } from 'lib/constants' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { router, combineUrl } from 'kea-router' +import { useButtonStyle } from 'scenes/authentication/useButtonStyles' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { SSOProvider } from '~/types' + +import { SocialLoginIcon } from './SocialLoginIcon' interface SocialLoginLinkProps { provider: SSOProvider @@ -113,6 +116,7 @@ interface SSOEnforcedLoginButtonProps { } export function SSOEnforcedLoginButton({ provider, email }: SSOEnforcedLoginButtonProps): JSX.Element { + const buttonStyles = useButtonStyle() return ( Log in with {SSO_PROVIDER_NAMES[provider]} diff --git a/frontend/src/lib/components/SocialLoginButton/SocialLoginIcon.tsx b/frontend/src/lib/components/SocialLoginButton/SocialLoginIcon.tsx index 6c1a905c40e35..dd64a8a1d9dce 100644 --- a/frontend/src/lib/components/SocialLoginButton/SocialLoginIcon.tsx +++ b/frontend/src/lib/components/SocialLoginButton/SocialLoginIcon.tsx @@ -1,4 +1,5 @@ import { GithubIcon, GitlabIcon, GoogleIcon, IconKey } from 'lib/lemon-ui/icons' + import { SSOProvider } from '~/types' export const SocialLoginIcon = (provider: SSOProvider): JSX.Element | undefined => { diff --git a/frontend/src/lib/components/StickyView/StickyView.tsx b/frontend/src/lib/components/StickyView/StickyView.tsx index a411236d10852..4aaf4bf2c7fed 100644 --- a/frontend/src/lib/components/StickyView/StickyView.tsx +++ b/frontend/src/lib/components/StickyView/StickyView.tsx @@ -1,6 +1,7 @@ +import './StickyView.scss' + import { useResizeObserver } from 'lib/hooks/useResizeObserver' import React, { useEffect, useRef, useState } from 'react' -import './StickyView.scss' export interface StickyViewProps { children: React.ReactNode diff --git a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx index c83ce034536d1..007b340af7d97 100644 --- a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx +++ b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.stories.tsx @@ -1,13 +1,15 @@ -import { useRef, useState } from 'react' import { Meta } from '@storybook/react' -import { SubscriptionsModal, SubscriptionsModalProps } from './SubscriptionsModal' -import { AvailableFeature, InsightShortId, Realm } from '~/types' -import preflightJson from '~/mocks/fixtures/_preflight.json' -import { useAvailableFeatures } from '~/mocks/features' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { uuid } from 'lib/utils' +import { useRef, useState } from 'react' + import { useStorybookMocks } from '~/mocks/browser' -import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useAvailableFeatures } from '~/mocks/features' +import preflightJson from '~/mocks/fixtures/_preflight.json' import { createMockSubscription, mockIntegration, mockSlackChannels } from '~/test/mocks' +import { AvailableFeature, InsightShortId, Realm } from '~/types' + +import { SubscriptionsModal, SubscriptionsModalProps } from './SubscriptionsModal' const meta: Meta = { title: 'Components/Subscriptions', diff --git a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.tsx b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.tsx index 0b9c2af78effa..25ba79553f949 100644 --- a/frontend/src/lib/components/Subscriptions/SubscriptionsModal.tsx +++ b/frontend/src/lib/components/Subscriptions/SubscriptionsModal.tsx @@ -1,14 +1,16 @@ -import { ManageSubscriptions } from './views/ManageSubscriptions' -import { EditSubscription } from './views/EditSubscription' +import { LemonButton, LemonButtonWithDropdown } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { router } from 'kea-router' -import { LemonButton, LemonButtonWithDropdown } from '@posthog/lemon-ui' -import { SubscriptionBaseProps, urlForSubscription, urlForSubscriptions } from './utils' -import { PayGatePage } from '../PayGatePage/PayGatePage' -import { AvailableFeature } from '~/types' -import { userLogic } from 'scenes/userLogic' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature } from '~/types' + +import { PayGatePage } from '../PayGatePage/PayGatePage' +import { SubscriptionBaseProps, urlForSubscription, urlForSubscriptions } from './utils' +import { EditSubscription } from './views/EditSubscription' +import { ManageSubscriptions } from './views/ManageSubscriptions' export interface SubscriptionsModalProps extends SubscriptionBaseProps { isOpen: boolean diff --git a/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts b/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts index a362f3dd7c5ad..820e8eb7d9786 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts @@ -1,9 +1,11 @@ +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' import { InsightShortId, SubscriptionType } from '~/types' + import { subscriptionLogic } from './subscriptionLogic' -import { router } from 'kea-router' const Insight1 = '1' as InsightShortId diff --git a/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts b/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts index 87c84e204c037..d6b65209c1e9b 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts @@ -1,20 +1,19 @@ import { connect, kea, key, listeners, path, props } from 'kea' -import { SubscriptionType } from '~/types' - -import api from 'lib/api' -import { loaders } from 'kea-loaders' import { forms } from 'kea-forms' - -import { isEmail, isURL } from 'lib/utils' +import { loaders } from 'kea-loaders' +import { beforeUnload, router, urlToAction } from 'kea-router' +import api from 'lib/api' import { dayjs } from 'lib/dayjs' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { beforeUnload, router, urlToAction } from 'kea-router' -import { subscriptionsLogic } from './subscriptionsLogic' +import { isEmail, isURL } from 'lib/utils' +import { getInsightId } from 'scenes/insights/utils' +import { integrationsLogic } from 'scenes/settings/project/integrationsLogic' + +import { SubscriptionType } from '~/types' import type { subscriptionLogicType } from './subscriptionLogicType' -import { getInsightId } from 'scenes/insights/utils' +import { subscriptionsLogic } from './subscriptionsLogic' import { SubscriptionBaseProps, urlForSubscription } from './utils' -import { integrationsLogic } from 'scenes/settings/project/integrationsLogic' const NEW_SUBSCRIPTION: Partial = { frequency: 'weekly', diff --git a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts index cfbc33b75fe4f..2cfbe7207bf52 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.test.ts @@ -1,7 +1,7 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' + import { useMocks } from '~/mocks/jest' -import { subscriptionsLogic } from './subscriptionsLogic' +import { initKeaTests } from '~/test/init' import { FilterType, InsightModel, @@ -12,6 +12,8 @@ import { SubscriptionType, } from '~/types' +import { subscriptionsLogic } from './subscriptionsLogic' + const Insight1 = '1' as InsightShortId const Insight2 = '2' as InsightShortId diff --git a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.ts b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.ts index fbfde31c7e69f..6c7ebab34f5d6 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionsLogic.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionsLogic.ts @@ -1,13 +1,12 @@ import { actions, afterMount, BreakPointFunction, kea, key, listeners, path, props, reducers } from 'kea' -import { SubscriptionType } from '~/types' - -import api from 'lib/api' import { loaders } from 'kea-loaders' - +import api from 'lib/api' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { getInsightId } from 'scenes/insights/utils' + +import { SubscriptionType } from '~/types' import type { subscriptionsLogicType } from './subscriptionsLogicType' -import { getInsightId } from 'scenes/insights/utils' import { SubscriptionBaseProps } from './utils' export const subscriptionsLogic = kea([ diff --git a/frontend/src/lib/components/Subscriptions/utils.tsx b/frontend/src/lib/components/Subscriptions/utils.tsx index 9ba2054c86d03..c058d119ff90b 100644 --- a/frontend/src/lib/components/Subscriptions/utils.tsx +++ b/frontend/src/lib/components/Subscriptions/utils.tsx @@ -1,9 +1,10 @@ import { LemonSelectOptions } from '@posthog/lemon-ui' +import { IconMail, IconSlack, IconSlackExternal } from 'lib/lemon-ui/icons' +import { LemonSelectMultipleOptionItem } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { range } from 'lib/utils' import { urls } from 'scenes/urls' + import { InsightShortId, SlackChannelType } from '~/types' -import { IconMail, IconSlack, IconSlackExternal } from 'lib/lemon-ui/icons' -import { LemonSelectMultipleOptionItem } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' export interface SubscriptionBaseProps { dashboardId?: number diff --git a/frontend/src/lib/components/Subscriptions/views/EditSubscription.tsx b/frontend/src/lib/components/Subscriptions/views/EditSubscription.tsx index 66f19b0bc54cb..53beeb522951d 100644 --- a/frontend/src/lib/components/Subscriptions/views/EditSubscription.tsx +++ b/frontend/src/lib/components/Subscriptions/views/EditSubscription.tsx @@ -1,20 +1,33 @@ -import { useEffect, useMemo } from 'react' +import { LemonDivider, LemonInput, LemonTextArea, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { Form } from 'kea-forms' +import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' +import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' +import { dayjs } from 'lib/dayjs' +import { Field } from 'lib/forms/Field' +import { IconChevronLeft } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { + LemonSelectMultiple, + LemonSelectMultipleOptionItem, +} from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { useEffect, useMemo } from 'react' import { membersLogic } from 'scenes/organization/membersLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { Field } from 'lib/forms/Field' -import { dayjs } from 'lib/dayjs' -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { integrationsLogic } from 'scenes/settings/project/integrationsLogic' +import { urls } from 'scenes/urls' + import { subscriptionLogic } from '../subscriptionLogic' -import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' -import { IconChevronLeft } from 'lib/lemon-ui/icons' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { subscriptionsLogic } from '../subscriptionsLogic' import { bysetposOptions, - frequencyOptionsSingular, frequencyOptionsPlural, + frequencyOptionsSingular, getSlackChannelOptions, intervalOptions, monthlyWeekdayOptions, @@ -23,18 +36,6 @@ import { timeOptions, weekdayOptions, } from '../utils' -import { LemonDivider, LemonInput, LemonTextArea, Link } from '@posthog/lemon-ui' -import { - LemonSelectMultiple, - LemonSelectMultipleOptionItem, -} from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' -import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' -import { urls } from 'scenes/urls' -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { Form } from 'kea-forms' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { integrationsLogic } from 'scenes/settings/project/integrationsLogic' interface EditSubscriptionProps extends SubscriptionBaseProps { id: number | 'new' diff --git a/frontend/src/lib/components/Subscriptions/views/ManageSubscriptions.tsx b/frontend/src/lib/components/Subscriptions/views/ManageSubscriptions.tsx index 50c782fc12682..b38b9f33977c2 100644 --- a/frontend/src/lib/components/Subscriptions/views/ManageSubscriptions.tsx +++ b/frontend/src/lib/components/Subscriptions/views/ManageSubscriptions.tsx @@ -1,13 +1,15 @@ import { useActions, useValues } from 'kea' -import { LemonButton, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' -import { SubscriptionType } from '~/types' -import { capitalizeFirstLetter, pluralize } from 'lib/utils' import { IconEllipsis, IconSlack } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { ProfileBubbles } from 'lib/lemon-ui/ProfilePicture' +import { capitalizeFirstLetter, pluralize } from 'lib/utils' + +import { SubscriptionType } from '~/types' + import { subscriptionsLogic } from '../subscriptionsLogic' import { SubscriptionBaseProps } from '../utils' -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' interface SubscriptionListItemProps { subscription: SubscriptionType diff --git a/frontend/src/lib/components/Support/SupportForm.tsx b/frontend/src/lib/components/Support/SupportForm.tsx index fa09fd9f1eaa7..c1ef770c9131f 100644 --- a/frontend/src/lib/components/Support/SupportForm.tsx +++ b/frontend/src/lib/components/Support/SupportForm.tsx @@ -1,13 +1,3 @@ -import { useActions, useValues } from 'kea' -import { SupportTicketKind, TARGET_AREA_TO_NAME, supportLogic } from './supportLogic' -import { Form } from 'kea-forms' -import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' -import { LemonSelect } from 'lib/lemon-ui/LemonSelect/LemonSelect' -import { Field } from 'lib/forms/Field' -import { IconBugReport, IconFeedback, IconHelpOutline } from 'lib/lemon-ui/icons' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput' -import { useRef } from 'react' import { LemonButton, LemonInput, @@ -15,9 +5,20 @@ import { LemonSegmentedButtonOption, lemonToast, } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { Form } from 'kea-forms' +import { Field } from 'lib/forms/Field' import { useUploadFiles } from 'lib/hooks/useUploadFiles' +import { IconBugReport, IconFeedback, IconHelpOutline } from 'lib/lemon-ui/icons' +import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect/LemonSelect' +import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { useRef } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { userLogic } from 'scenes/userLogic' +import { supportLogic, SupportTicketKind, TARGET_AREA_TO_NAME } from './supportLogic' + const SUPPORT_TICKET_OPTIONS: LemonSegmentedButtonOption[] = [ { value: 'support', diff --git a/frontend/src/lib/components/Support/SupportModal.tsx b/frontend/src/lib/components/Support/SupportModal.tsx index 6a963ae6b04e9..a605c82213cb8 100644 --- a/frontend/src/lib/components/Support/SupportModal.tsx +++ b/frontend/src/lib/components/Support/SupportModal.tsx @@ -1,9 +1,10 @@ import { useActions, useValues } from 'kea' -import { supportLogic } from './supportLogic' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { LemonModal } from 'lib/lemon-ui/LemonModal/LemonModal' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { SupportForm, SupportFormButtons } from './SupportForm' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { supportLogic } from './supportLogic' export function SupportModal({ loggedIn = true }: { loggedIn?: boolean }): JSX.Element | null { const { sendSupportRequest, isSupportFormOpen, sendSupportLoggedOutRequest, title } = useValues(supportLogic) diff --git a/frontend/src/lib/components/Support/supportLogic.ts b/frontend/src/lib/components/Support/supportLogic.ts index af00389dcf7bf..076e44e0ffbd9 100644 --- a/frontend/src/lib/components/Support/supportLogic.ts +++ b/frontend/src/lib/components/Support/supportLogic.ts @@ -1,18 +1,19 @@ +import { captureException } from '@sentry/react' +import * as Sentry from '@sentry/react' import { actions, connect, kea, listeners, path, props, reducers, selectors } from 'kea' -import { userLogic } from 'scenes/userLogic' - -import type { supportLogicType } from './supportLogicType' import { forms } from 'kea-forms' -import { Region, SidePanelTab, TeamType, UserType } from '~/types' +import { actionToUrl, router, urlToAction } from 'kea-router' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { uuid } from 'lib/utils' import posthog from 'posthog-js' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { actionToUrl, router, urlToAction } from 'kea-router' -import { captureException } from '@sentry/react' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { teamLogic } from 'scenes/teamLogic' -import * as Sentry from '@sentry/react' +import { userLogic } from 'scenes/userLogic' + import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic' +import { Region, SidePanelTab, TeamType, UserType } from '~/types' + +import type { supportLogicType } from './supportLogicType' function getSessionReplayLink(): string { const link = posthog diff --git a/frontend/src/lib/components/TZLabel/index.tsx b/frontend/src/lib/components/TZLabel/index.tsx index ed6c3b4ac5bbb..2e4c1dcb3c3c9 100644 --- a/frontend/src/lib/components/TZLabel/index.tsx +++ b/frontend/src/lib/components/TZLabel/index.tsx @@ -1,17 +1,19 @@ import './index.scss' -import { useActions, useValues } from 'kea' + // eslint-disable-next-line no-restricted-imports -import { ProjectOutlined, LaptopOutlined } from '@ant-design/icons' +import { LaptopOutlined, ProjectOutlined } from '@ant-design/icons' +import { LemonButton, LemonDivider, LemonDropdown, LemonDropdownProps } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { dayjs } from 'lib/dayjs' +import { IconSettings, IconWeb } from 'lib/lemon-ui/icons' import { humanFriendlyDetailedTime, shortTimeZone } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { teamLogic } from '../../../scenes/teamLogic' -import { dayjs } from 'lib/dayjs' -import clsx from 'clsx' import React, { useCallback, useEffect, useMemo, useState } from 'react' -import { LemonButton, LemonDivider, LemonDropdown, LemonDropdownProps } from '@posthog/lemon-ui' -import { IconSettings, IconWeb } from 'lib/lemon-ui/icons' import { urls } from 'scenes/urls' +import { teamLogic } from '../../../scenes/teamLogic' + const BASE_OUTPUT_FORMAT = 'ddd, MMM D, YYYY h:mm A' const BASE_OUTPUT_FORMAT_WITH_SECONDS = 'ddd, MMM D, YYYY h:mm:ss A' diff --git a/frontend/src/lib/components/Table/Table.tsx b/frontend/src/lib/components/Table/Table.tsx index bc8a41e524eec..7cf69790d2763 100644 --- a/frontend/src/lib/components/Table/Table.tsx +++ b/frontend/src/lib/components/Table/Table.tsx @@ -1,10 +1,11 @@ -import { uniqueBy } from 'lib/utils' +import { ColumnType } from 'antd/lib/table' import { useValues } from 'kea' -import { userLogic } from 'scenes/userLogic' -import { TZLabel } from '../TZLabel' import { normalizeColumnTitle } from 'lib/components/Table/utils' -import { ColumnType } from 'antd/lib/table' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { uniqueBy } from 'lib/utils' +import { userLogic } from 'scenes/userLogic' + +import { TZLabel } from '../TZLabel' export function createdAtColumn = Record>(): ColumnType { return { diff --git a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx index 408f7fc7e926b..643009749e856 100644 --- a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx @@ -1,28 +1,31 @@ import './InfiniteList.scss' import '../../lemon-ui/Popover/Popover.scss' + +import { LemonTag } from '@posthog/lemon-ui' import { Empty } from 'antd' -import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' -import { List, ListRowProps, ListRowRenderer } from 'react-virtualized/dist/es/List' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' -import { infiniteListLogic, NO_ITEM_SELECTED } from './infiniteListLogic' +import { ControlledDefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopoverContents' +import { definitionPopoverLogic } from 'lib/components/DefinitionPopover/definitionPopoverLogic' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' import { TaxonomicDefinitionTypes, TaxonomicFilterGroup, TaxonomicFilterGroupType, } from 'lib/components/TaxonomicFilter/types' -import { EventDefinition, PropertyDefinition } from '~/types' -import { dayjs } from 'lib/dayjs' import { STALE_EVENT_SECONDS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import clsx from 'clsx' -import { definitionPopoverLogic } from 'lib/components/DefinitionPopover/definitionPopoverLogic' -import { ControlledDefinitionPopover } from 'lib/components/DefinitionPopover/DefinitionPopoverContents' import { pluralize } from 'lib/utils' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { useState } from 'react' -import { LemonTag } from '@posthog/lemon-ui' +import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' +import { List, ListRowProps, ListRowRenderer } from 'react-virtualized/dist/es/List' + +import { EventDefinition, PropertyDefinition } from '~/types' + +import { infiniteListLogic, NO_ITEM_SELECTED } from './infiniteListLogic' export interface InfiniteListProps { popupAnchorElement: HTMLDivElement | null diff --git a/frontend/src/lib/components/TaxonomicFilter/InfiniteSelectResults.tsx b/frontend/src/lib/components/TaxonomicFilter/InfiniteSelectResults.tsx index 777e11cf0c6ff..9aa893e8be895 100644 --- a/frontend/src/lib/components/TaxonomicFilter/InfiniteSelectResults.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/InfiniteSelectResults.tsx @@ -1,10 +1,11 @@ import { Tag } from 'antd' +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' -import { taxonomicFilterLogic } from './taxonomicFilterLogic' -import { infiniteListLogic } from 'lib/components/TaxonomicFilter/infiniteListLogic' import { InfiniteList } from 'lib/components/TaxonomicFilter/InfiniteList' +import { infiniteListLogic } from 'lib/components/TaxonomicFilter/infiniteListLogic' import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps } from 'lib/components/TaxonomicFilter/types' -import clsx from 'clsx' + +import { taxonomicFilterLogic } from './taxonomicFilterLogic' export interface InfiniteSelectResultsProps { focusInput: () => void diff --git a/frontend/src/lib/components/TaxonomicFilter/InlineHogQLEditor.tsx b/frontend/src/lib/components/TaxonomicFilter/InlineHogQLEditor.tsx index e7d35c4c18d77..21953476b8a53 100644 --- a/frontend/src/lib/components/TaxonomicFilter/InlineHogQLEditor.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/InlineHogQLEditor.tsx @@ -1,5 +1,5 @@ -import { TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor' +import { TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' export interface InlineHogQLEditorProps { value?: TaxonomicFilterValue diff --git a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.stories.tsx b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.stories.tsx index a26af339ab0bc..b6075ccfa8f91 100644 --- a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.stories.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.stories.tsx @@ -1,14 +1,16 @@ -import { TaxonomicFilter } from './TaxonomicFilter' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator' +import { Meta, StoryFn } from '@storybook/react' import { useActions, useMountedLogic } from 'kea' -import { actionsModel } from '~/models/actionsModel' +import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { useEffect } from 'react' -import { infiniteListLogic } from './infiniteListLogic' -import { Meta, StoryFn } from '@storybook/react' + import { useAvailableFeatures } from '~/mocks/features' +import { actionsModel } from '~/models/actionsModel' import { AvailableFeature } from '~/types' +import { infiniteListLogic } from './infiniteListLogic' +import { TaxonomicFilter } from './TaxonomicFilter' + const meta: Meta = { title: 'Filters/Taxonomic Filter', component: TaxonomicFilter, diff --git a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx index 9d74f6d1ffb24..0c3b7173e9782 100644 --- a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx @@ -1,17 +1,19 @@ import './TaxonomicFilter.scss' -import { useEffect, useMemo, useRef } from 'react' + +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' -import { InfiniteSelectResults } from './InfiniteSelectResults' -import { taxonomicFilterLogic } from './taxonomicFilterLogic' import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps, TaxonomicFilterProps, } from 'lib/components/TaxonomicFilter/types' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { IconKeyboard } from 'lib/lemon-ui/icons' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import clsx from 'clsx' +import { useEffect, useMemo, useRef } from 'react' + +import { InfiniteSelectResults } from './InfiniteSelectResults' +import { taxonomicFilterLogic } from './taxonomicFilterLogic' let uniqueMemoizedIndex = 0 diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts index 5ce37452c1e2a..8a524a014d281 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts @@ -1,11 +1,13 @@ -import { infiniteListLogic } from './infiniteListLogic' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { MOCK_TEAM_ID } from 'lib/api.mock' import { expectLogic, partial } from 'kea-test-utils' +import { MOCK_TEAM_ID } from 'lib/api.mock' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' + +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' import { mockEventDefinitions, mockEventPropertyDefinitions } from '~/test/mocks' import { AppContext, PropertyDefinition } from '~/types' -import { useMocks } from '~/mocks/jest' + +import { infiniteListLogic } from './infiniteListLogic' window.POSTHOG_APP_CONTEXT = { current_team: { id: MOCK_TEAM_ID } } as unknown as AppContext diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts index 033a3cc08db21..4d0857b0878fc 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts @@ -1,11 +1,9 @@ +import Fuse from 'fuse.js' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, events } from 'kea' import { combineUrl } from 'kea-router' import api from 'lib/api' -import { RenderedRows } from 'react-virtualized/dist/es/List' -import type { infiniteListLogicType } from './infiniteListLogicType' -import { CohortType, EventDefinition } from '~/types' -import Fuse from 'fuse.js' +import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' import { InfiniteListLogicProps, ListFuse, @@ -15,11 +13,15 @@ import { TaxonomicFilterGroup, TaxonomicFilterGroupType, } from 'lib/components/TaxonomicFilter/types' -import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' -import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' import { getKeyMapping } from 'lib/taxonomy' +import { RenderedRows } from 'react-virtualized/dist/es/List' +import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' + +import { CohortType, EventDefinition } from '~/types' + import { teamLogic } from '../../../scenes/teamLogic' import { captureTimeToSeeData } from '../../internalMetrics' +import type { infiniteListLogicType } from './infiniteListLogicType' /* by default the pop-up starts open for the first item in the list diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts index 4237bea8c6453..2c6f0ff84c2db 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.test.ts @@ -1,14 +1,16 @@ -import { infiniteListLogic } from './infiniteListLogic' -import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps } from 'lib/components/TaxonomicFilter/types' -import { MOCK_TEAM_ID } from 'lib/api.mock' import { expectLogic } from 'kea-test-utils' +import { MOCK_TEAM_ID } from 'lib/api.mock' +import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' +import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps } from 'lib/components/TaxonomicFilter/types' + +import { useMocks } from '~/mocks/jest' +import { actionsModel } from '~/models/actionsModel' +import { groupsModel } from '~/models/groupsModel' import { initKeaTests } from '~/test/init' import { mockEventDefinitions } from '~/test/mocks' import { AppContext } from '~/types' -import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' -import { groupsModel } from '~/models/groupsModel' -import { actionsModel } from '~/models/actionsModel' -import { useMocks } from '~/mocks/jest' + +import { infiniteListLogic } from './infiniteListLogic' window.POSTHOG_APP_CONTEXT = { current_team: { id: MOCK_TEAM_ID } } as unknown as AppContext diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx index 7bcd6833e25ab..1cfba27aa602b 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx @@ -1,5 +1,7 @@ -import { BuiltLogic, kea, props, key, path, connect, actions, reducers, selectors, listeners } from 'kea' -import type { taxonomicFilterLogicType } from './taxonomicFilterLogicType' +import { actions, BuiltLogic, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { combineUrl } from 'kea-router' +import { infiniteListLogic } from 'lib/components/TaxonomicFilter/infiniteListLogic' +import { infiniteListLogicType } from 'lib/components/TaxonomicFilter/infiniteListLogicType' import { ListStorage, SimpleOption, @@ -8,41 +10,41 @@ import { TaxonomicFilterLogicProps, TaxonomicFilterValue, } from 'lib/components/TaxonomicFilter/types' -import { infiniteListLogic } from 'lib/components/TaxonomicFilter/infiniteListLogic' +import { IconCohort } from 'lib/lemon-ui/icons' +import { KEY_MAPPING } from 'lib/taxonomy' +import { capitalizeFirstLetter, pluralize, toParams } from 'lib/utils' +import { getEventDefinitionIcon, getPropertyDefinitionIcon } from 'scenes/data-management/events/DefinitionHeader' +import { experimentsLogic } from 'scenes/experiments/experimentsLogic' +import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' +import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { actionsModel } from '~/models/actionsModel' +import { cohortsModel } from '~/models/cohortsModel' +import { dashboardsModel } from '~/models/dashboardsModel' +import { groupPropertiesModel } from '~/models/groupPropertiesModel' +import { groupsModel } from '~/models/groupsModel' +import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' import { ActionType, CohortType, - EventDefinitionType, DashboardType, EventDefinition, + EventDefinitionType, Experiment, FeatureFlagType, Group, InsightModel, + NotebookType, PersonProperty, PersonType, PluginType, PropertyDefinition, - NotebookType, } from '~/types' -import { cohortsModel } from '~/models/cohortsModel' -import { actionsModel } from '~/models/actionsModel' -import { teamLogic } from 'scenes/teamLogic' -import { groupsModel } from '~/models/groupsModel' -import { groupPropertiesModel } from '~/models/groupPropertiesModel' -import { capitalizeFirstLetter, pluralize, toParams } from 'lib/utils' -import { combineUrl } from 'kea-router' -import { IconCohort } from 'lib/lemon-ui/icons' -import { KEY_MAPPING } from 'lib/taxonomy' -import { getEventDefinitionIcon, getPropertyDefinitionIcon } from 'scenes/data-management/events/DefinitionHeader' -import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' -import { experimentsLogic } from 'scenes/experiments/experimentsLogic' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { dashboardsModel } from '~/models/dashboardsModel' -import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { infiniteListLogicType } from 'lib/components/TaxonomicFilter/infiniteListLogicType' -import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' + import { InlineHogQLEditor } from './InlineHogQLEditor' +import type { taxonomicFilterLogicType } from './taxonomicFilterLogicType' export const eventTaxonomicGroupProps: Pick = { getPopoverHeader: (eventDefinition: EventDefinition): string => { diff --git a/frontend/src/lib/components/TaxonomicFilter/types.ts b/frontend/src/lib/components/TaxonomicFilter/types.ts index 3de701a308c3e..3dbb5c0eabc38 100644 --- a/frontend/src/lib/components/TaxonomicFilter/types.ts +++ b/frontend/src/lib/components/TaxonomicFilter/types.ts @@ -1,6 +1,7 @@ +import Fuse from 'fuse.js' import { LogicWrapper } from 'kea' + import { ActionType, CohortType, EventDefinition, PersonProperty, PropertyDefinition } from '~/types' -import Fuse from 'fuse.js' export interface SimpleOption { name: string diff --git a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.stories.tsx b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.stories.tsx index fb8835de130df..881c45b52bd50 100644 --- a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.stories.tsx +++ b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.stories.tsx @@ -1,11 +1,13 @@ -import { useState } from 'react' -import { TaxonomicPopover, TaxonomicStringPopover } from './TaxonomicPopover' -import { cohortsModel } from '~/models/cohortsModel' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { Meta } from '@storybook/react' import { useMountedLogic } from 'kea' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator' -import { Meta } from '@storybook/react' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { useState } from 'react' + +import { cohortsModel } from '~/models/cohortsModel' + +import { TaxonomicPopover, TaxonomicStringPopover } from './TaxonomicPopover' const meta: Meta = { title: 'Filters/TaxonomicPopover', diff --git a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx index abf4589005691..2d0e835d7ef85 100644 --- a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx +++ b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx @@ -1,9 +1,9 @@ import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' -import { useEffect, useState } from 'react' -import { LemonButton, LemonButtonProps, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' import { IconClose } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' import { LemonDropdown } from 'lib/lemon-ui/LemonDropdown' +import { useEffect, useState } from 'react' export interface TaxonomicPopoverProps extends Omit { diff --git a/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.tsx b/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.tsx index f343686d66a2e..26fc8507bbea4 100644 --- a/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.tsx +++ b/frontend/src/lib/components/TimelineSeekbar/TimelineSeekbar.tsx @@ -1,11 +1,12 @@ +import './TimelineSeekbar.scss' + import { LemonBadge } from '@posthog/lemon-ui' import clsx from 'clsx' import { Dayjs } from 'lib/dayjs' -import { humanFriendlyDetailedTime, pluralize } from 'lib/utils' -import { AlignType } from 'rc-trigger/lib/interface' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import './TimelineSeekbar.scss' +import { humanFriendlyDetailedTime, pluralize } from 'lib/utils' +import { AlignType } from 'rc-trigger/lib/interface' export interface TimelinePoint { timestamp: Dayjs diff --git a/frontend/src/lib/components/UnitPicker/CustomUnitModal.tsx b/frontend/src/lib/components/UnitPicker/CustomUnitModal.tsx index 35f30b91f3241..8491aafb0c61d 100644 --- a/frontend/src/lib/components/UnitPicker/CustomUnitModal.tsx +++ b/frontend/src/lib/components/UnitPicker/CustomUnitModal.tsx @@ -1,10 +1,11 @@ -import { RefCallback, useEffect, useState } from 'react' -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { HandleUnitChange } from 'lib/components/UnitPicker/UnitPicker' import { PureField } from 'lib/forms/Field' -import { capitalizeFirstLetter } from 'lib/utils' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { HandleUnitChange } from 'lib/components/UnitPicker/UnitPicker' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { capitalizeFirstLetter } from 'lib/utils' +import { RefCallback, useEffect, useState } from 'react' + import { TrendsFilter } from '~/queries/schema' function chooseFormativeElementValue( diff --git a/frontend/src/lib/components/UnitPicker/UnitPicker.tsx b/frontend/src/lib/components/UnitPicker/UnitPicker.tsx index f9c20b279fed5..79a877f9d316f 100644 --- a/frontend/src/lib/components/UnitPicker/UnitPicker.tsx +++ b/frontend/src/lib/components/UnitPicker/UnitPicker.tsx @@ -1,11 +1,11 @@ -import { AggregationAxisFormat, INSIGHT_UNIT_OPTIONS } from 'scenes/insights/aggregationAxisFormat' -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { useMemo, useRef, useState } from 'react' import { useActions, useValues } from 'kea' +import { CustomUnitModal } from 'lib/components/UnitPicker/CustomUnitModal' import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { CustomUnitModal } from 'lib/components/UnitPicker/CustomUnitModal' +import { useMemo, useRef, useState } from 'react' +import { AggregationAxisFormat, INSIGHT_UNIT_OPTIONS } from 'scenes/insights/aggregationAxisFormat' import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' diff --git a/frontend/src/lib/components/UniversalSearch/UniversalSearchPopover.tsx b/frontend/src/lib/components/UniversalSearch/UniversalSearchPopover.tsx index cbacb2ef845b3..d7f58130d3c7e 100644 --- a/frontend/src/lib/components/UniversalSearch/UniversalSearchPopover.tsx +++ b/frontend/src/lib/components/UniversalSearch/UniversalSearchPopover.tsx @@ -1,10 +1,18 @@ import './UniversalSearch.scss' -import { useState } from 'react' + +import clsx from 'clsx' +import { useMountedLogic, useValues } from 'kea' +import { combineUrl, router } from 'kea-router' +import { useEventListener } from 'lib/hooks/useEventListener' import { LemonButtonWithDropdownProps } from 'lib/lemon-ui/LemonButton' -import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps, TaxonomicFilterValue } from '../TaxonomicFilter/types' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { Popover } from 'lib/lemon-ui/Popover' -import { combineUrl, router } from 'kea-router' +import { useState } from 'react' +import { experimentsLogic } from 'scenes/experiments/experimentsLogic' +import { PluginSelectionType, pluginsLogic } from 'scenes/plugins/pluginsLogic' import { urls } from 'scenes/urls' + +import { navigationLogic } from '~/layout/navigation/navigationLogic' import { ActionType, ChartDisplayType, @@ -17,15 +25,10 @@ import { InsightType, PersonType, } from '~/types' -import { PluginSelectionType, pluginsLogic } from 'scenes/plugins/pluginsLogic' -import clsx from 'clsx' -import { navigationLogic } from '~/layout/navigation/navigationLogic' -import { useMountedLogic, useValues } from 'kea' -import { useEventListener } from 'lib/hooks/useEventListener' -import { taxonomicFilterLogic } from '../TaxonomicFilter/taxonomicFilterLogic' + import { TaxonomicFilter } from '../TaxonomicFilter/TaxonomicFilter' -import { experimentsLogic } from 'scenes/experiments/experimentsLogic' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { taxonomicFilterLogic } from '../TaxonomicFilter/taxonomicFilterLogic' +import { TaxonomicFilterGroupType, TaxonomicFilterLogicProps, TaxonomicFilterValue } from '../TaxonomicFilter/types' export interface UniversalSearchPopoverProps extends Omit { diff --git a/frontend/src/lib/components/UserActivityIndicator/UserActivityIndicator.tsx b/frontend/src/lib/components/UserActivityIndicator/UserActivityIndicator.tsx index 6030276e756e9..a01ae79e4c7be 100644 --- a/frontend/src/lib/components/UserActivityIndicator/UserActivityIndicator.tsx +++ b/frontend/src/lib/components/UserActivityIndicator/UserActivityIndicator.tsx @@ -1,8 +1,11 @@ +import './UserActivityIndicator.scss' + import clsx from 'clsx' -import { UserBasicType } from '~/types' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' + +import { UserBasicType } from '~/types' + import { TZLabel } from '../TZLabel' -import './UserActivityIndicator.scss' export interface UserActivityIndicatorProps { prefix?: string diff --git a/frontend/src/lib/components/UserSelectItem.tsx b/frontend/src/lib/components/UserSelectItem.tsx index 9167d87fe3bc5..0732418c35479 100644 --- a/frontend/src/lib/components/UserSelectItem.tsx +++ b/frontend/src/lib/components/UserSelectItem.tsx @@ -1,7 +1,8 @@ -import { UserBasicType, UserType } from '~/types' import { LemonSelectMultipleOptionItem } from 'lib/lemon-ui/LemonSelectMultiple' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { UserBasicType, UserType } from '~/types' + export interface UserSelectItemProps { user: UserBasicType | UserType } diff --git a/frontend/src/lib/components/VersionChecker/VersionCheckerBanner.tsx b/frontend/src/lib/components/VersionChecker/VersionCheckerBanner.tsx index dcbd1ca4255d4..38094c08dd21f 100644 --- a/frontend/src/lib/components/VersionChecker/VersionCheckerBanner.tsx +++ b/frontend/src/lib/components/VersionChecker/VersionCheckerBanner.tsx @@ -1,12 +1,17 @@ import { useValues } from 'kea' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' + import { versionCheckerLogic } from './versionCheckerLogic' -export function VersionCheckerBanner(): JSX.Element { +export function VersionCheckerBanner({ minVersionAccepted }: { minVersionAccepted?: string }): JSX.Element { const { versionWarning } = useValues(versionCheckerLogic) - // We don't want to show a message if the diff is too small (we might be still deploying the changes out) - if (!versionWarning || versionWarning.diff < 5) { + if ( + !versionWarning || + (minVersionAccepted && versionWarning.currentVersion + ? versionWarning.currentVersion.localeCompare(minVersionAccepted) >= 0 + : versionWarning.diff < 5) + ) { return <> } diff --git a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts index 22431153935d2..9d3ab79d0d3ce 100644 --- a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts +++ b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.test.ts @@ -1,7 +1,9 @@ +import { expectLogic } from 'kea-test-utils' + import { useMocks } from '~/mocks/jest' -import { SDKVersion, versionCheckerLogic } from './versionCheckerLogic' import { initKeaTests } from '~/test/init' -import { expectLogic } from 'kea-test-utils' + +import { SDKVersion, versionCheckerLogic } from './versionCheckerLogic' const useMockedVersions = (githubVersions: SDKVersion[], usedVersions: SDKVersion[]): void => { useMocks({ diff --git a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.ts b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.ts index acc1897bc78f2..875c27ae97a8a 100644 --- a/frontend/src/lib/components/VersionChecker/versionCheckerLogic.ts +++ b/frontend/src/lib/components/VersionChecker/versionCheckerLogic.ts @@ -1,6 +1,7 @@ import { actions, afterMount, kea, listeners, path, reducers, sharedListeners } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' + import { HogQLQuery, NodeKind } from '~/queries/schema' import type { versionCheckerLogicType } from './versionCheckerLogicType' diff --git a/frontend/src/lib/components/VisibilitySensor/VisibilitySensor.tsx b/frontend/src/lib/components/VisibilitySensor/VisibilitySensor.tsx index 8ff2d988e37ef..97a3fa0fd5820 100644 --- a/frontend/src/lib/components/VisibilitySensor/VisibilitySensor.tsx +++ b/frontend/src/lib/components/VisibilitySensor/VisibilitySensor.tsx @@ -1,5 +1,6 @@ import { useActions } from 'kea' import { useEffect, useRef } from 'react' + import { visibilitySensorLogic } from './visibilitySensorLogic' interface VisibilityProps { diff --git a/frontend/src/lib/components/VisibilitySensor/visibilitySensorLogic.tsx b/frontend/src/lib/components/VisibilitySensor/visibilitySensorLogic.tsx index ce0bdf419953d..835d9a98df30e 100644 --- a/frontend/src/lib/components/VisibilitySensor/visibilitySensorLogic.tsx +++ b/frontend/src/lib/components/VisibilitySensor/visibilitySensorLogic.tsx @@ -1,5 +1,5 @@ +import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { windowValues } from 'kea-window-values' -import { kea, props, key, path, actions, reducers, selectors, listeners } from 'kea' import type { visibilitySensorLogicType } from './visibilitySensorLogicType' export const visibilitySensorLogic = kea([ diff --git a/frontend/src/lib/components/hedgehogs.stories.tsx b/frontend/src/lib/components/hedgehogs.stories.tsx index 3abe96b5fdb9e..0ae1a23b4e39b 100644 --- a/frontend/src/lib/components/hedgehogs.stories.tsx +++ b/frontend/src/lib/components/hedgehogs.stories.tsx @@ -1,5 +1,6 @@ -import { Meta } from '@storybook/react' import { LemonTable } from '@posthog/lemon-ui' +import { Meta } from '@storybook/react' + import * as hedgehogs from './hedgehogs' interface HedgehogDefinition { @@ -14,8 +15,8 @@ const allHedgehogs: HedgehogDefinition[] = Object.entries(hedgehogs).map(([key, const meta: Meta = { title: 'Lemon UI/Hog illustrations', + tags: ['test-skip', 'autodocs'], // Not valuable to take snapshots of these hedgehogs parameters: { - testOptions: { skip: true }, // Not valuable to take snapshots of these hedgehogs docs: { description: { component: ` @@ -37,7 +38,6 @@ she will get to it dependant on work load. }, }, }, - tags: ['autodocs'], } export default meta export function Library(): JSX.Element { diff --git a/frontend/src/lib/components/hedgehogs.tsx b/frontend/src/lib/components/hedgehogs.tsx index f5b8d0fe64097..ddc41d7bf8c4c 100644 --- a/frontend/src/lib/components/hedgehogs.tsx +++ b/frontend/src/lib/components/hedgehogs.tsx @@ -1,36 +1,36 @@ // Loads custom icons (some icons may come from a third-party library) -import { ImgHTMLAttributes } from 'react' -import surprisedHog from 'public/hedgehog/surprised-hog.png' -import xRayHog from 'public/hedgehog/x-ray-hog.png' -import hospitalHog from 'public/hedgehog/hospital-hog.png' import blushingHog from 'public/hedgehog/blushing-hog.png' -import laptopHog1 from 'public/hedgehog/laptop-hog-01.png' -import laptopHog2 from 'public/hedgehog/laptop-hog-02.png' -import explorerHog from 'public/hedgehog/explorer-hog.png' -import runningHog from 'public/hedgehog/running-hog.png' -import spaceHog from 'public/hedgehog/space-hog.png' -import tronHog from 'public/hedgehog/tron-hog.png' -import heartHog from 'public/hedgehog/heart-hog.png' -import starHog from 'public/hedgehog/star-hog.png' -import policeHog from 'public/hedgehog/police-hog.png' -import sleepingHog from 'public/hedgehog/sleeping-hog.png' import builderHog1 from 'public/hedgehog/builder-hog-01.png' import builderHog2 from 'public/hedgehog/builder-hog-02.png' import builderHog3 from 'public/hedgehog/builder-hog-03.png' -import professorHog from 'public/hedgehog/professor-hog.png' -import supportHeroHog from 'public/hedgehog/support-hero-hog.png' -import xRayHog2 from 'public/hedgehog/x-ray-hogs-02.png' +import detectiveHog from 'public/hedgehog/detective-hog.png' +import experimentsHog from 'public/hedgehog/experiments-hog.png' +import explorerHog from 'public/hedgehog/explorer-hog.png' +import featureFlagHog from 'public/hedgehog/feature-flag-hog.png' +import heartHog from 'public/hedgehog/heart-hog.png' +import hospitalHog from 'public/hedgehog/hospital-hog.png' +import laptopHog1 from 'public/hedgehog/laptop-hog-01.png' +import laptopHog2 from 'public/hedgehog/laptop-hog-02.png' import laptopHog3 from 'public/hedgehog/laptop-hog-03.png' import laptopHog4 from 'public/hedgehog/laptop-hog-04.png' import laptopHogEU from 'public/hedgehog/laptop-hog-eu.png' -import detectiveHog from 'public/hedgehog/detective-hog.png' -import mailHog from 'public/hedgehog/mail-hog.png' -import featureFlagHog from 'public/hedgehog/feature-flag-hog.png' -import experimentsHog from 'public/hedgehog/experiments-hog.png' import listHog from 'public/hedgehog/list-hog.png' -import warningHog from 'public/hedgehog/warning-hog.png' -import readingHog from 'public/hedgehog/reading-hog.png' +import mailHog from 'public/hedgehog/mail-hog.png' import microphoneHog from 'public/hedgehog/microphone-hog.png' +import policeHog from 'public/hedgehog/police-hog.png' +import professorHog from 'public/hedgehog/professor-hog.png' +import readingHog from 'public/hedgehog/reading-hog.png' +import runningHog from 'public/hedgehog/running-hog.png' +import sleepingHog from 'public/hedgehog/sleeping-hog.png' +import spaceHog from 'public/hedgehog/space-hog.png' +import starHog from 'public/hedgehog/star-hog.png' +import supportHeroHog from 'public/hedgehog/support-hero-hog.png' +import surprisedHog from 'public/hedgehog/surprised-hog.png' +import tronHog from 'public/hedgehog/tron-hog.png' +import warningHog from 'public/hedgehog/warning-hog.png' +import xRayHog from 'public/hedgehog/x-ray-hog.png' +import xRayHog2 from 'public/hedgehog/x-ray-hogs-02.png' +import { ImgHTMLAttributes } from 'react' type HedgehogProps = Omit, 'src'> diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 712c7c77fa957..69c5a65aaa2f8 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -176,6 +176,7 @@ export const FEATURE_FLAGS = { PERSON_FEED_CANVAS: 'person-feed-canvas', // owner: #project-canvas MULTI_PROJECT_FEATURE_FLAGS: 'multi-project-feature-flags', // owner: @jurajmajerik #team-feature-success NETWORK_PAYLOAD_CAPTURE: 'network-payload-capture', // owner: #team-monitoring + FEATURE_FLAG_COHORT_CREATION: 'feature-flag-cohort-creation', // owner: @neilkakkar #team-feature-success INSIGHT_HORIZONTAL_CONTROLS: 'insight-horizontal-controls', // owner: @benjackwhite } as const export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS] diff --git a/frontend/src/lib/dayjs.ts b/frontend/src/lib/dayjs.ts index 51bf082205b51..6dac103d72ff2 100644 --- a/frontend/src/lib/dayjs.ts +++ b/frontend/src/lib/dayjs.ts @@ -1,13 +1,13 @@ // eslint-disable-next-line no-restricted-imports import dayjs, { Dayjs as DayjsOriginal, isDayjs } from 'dayjs' -import LocalizedFormat from 'dayjs/plugin/localizedFormat' -import relativeTime from 'dayjs/plugin/relativeTime' +import duration from 'dayjs/plugin/duration' import isSameOrAfter from 'dayjs/plugin/isSameOrAfter' import isSameOrBefore from 'dayjs/plugin/isSameOrBefore' +import LocalizedFormat from 'dayjs/plugin/localizedFormat' +import quarterOfYear from 'dayjs/plugin/quarterOfYear' +import relativeTime from 'dayjs/plugin/relativeTime' import timezone from 'dayjs/plugin/timezone' import utc from 'dayjs/plugin/utc' -import duration from 'dayjs/plugin/duration' -import quarterOfYear from 'dayjs/plugin/quarterOfYear' // necessary for any localized date formatting to work dayjs.extend(LocalizedFormat) @@ -21,7 +21,7 @@ dayjs.extend(quarterOfYear) const now = (): Dayjs => dayjs() -export { dayjs, now, isDayjs } +export { dayjs, isDayjs, now } /** Parse UTC datetime string using Day.js, taking into account time zone conversion edge cases. */ export function dayjsUtcToTimezone( diff --git a/frontend/src/lib/forms/Field.stories.tsx b/frontend/src/lib/forms/Field.stories.tsx index d5d071ba89d9d..34c229681c114 100644 --- a/frontend/src/lib/forms/Field.stories.tsx +++ b/frontend/src/lib/forms/Field.stories.tsx @@ -1,9 +1,9 @@ -import { Meta } from '@storybook/react' -import { Field, PureField } from './Field' import { LemonButton, LemonCheckbox, LemonInput, LemonSelect, LemonTextArea } from '@posthog/lemon-ui' +import { Meta } from '@storybook/react' import { kea, path, useAllValues } from 'kea' import { Form, forms } from 'kea-forms' +import { Field, PureField } from './Field' import type { formLogicType } from './Field.storiesType' const meta: Meta = { diff --git a/frontend/src/lib/forms/Field.tsx b/frontend/src/lib/forms/Field.tsx index 6fdc536be363f..b0924ae43ae5b 100644 --- a/frontend/src/lib/forms/Field.tsx +++ b/frontend/src/lib/forms/Field.tsx @@ -1,7 +1,7 @@ +import clsx from 'clsx' +import { Field as KeaField, FieldProps as KeaFieldProps } from 'kea-forms/lib/components' import { IconErrorOutline } from 'lib/lemon-ui/icons' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { Field as KeaField, FieldProps as KeaFieldProps } from 'kea-forms/lib/components' -import clsx from 'clsx' export type PureFieldProps = { /** The label name to be displayed */ diff --git a/frontend/src/lib/hooks/useBreakpoint.ts b/frontend/src/lib/hooks/useBreakpoint.ts index 1592c09b6781e..cf01e7d80e925 100644 --- a/frontend/src/lib/hooks/useBreakpoint.ts +++ b/frontend/src/lib/hooks/useBreakpoint.ts @@ -1,6 +1,7 @@ +import { getActiveBreakpointValue } from 'lib/utils/responsiveUtils' import { useEffect, useState } from 'react' + import { useWindowSize } from './useWindowSize' -import { getActiveBreakpointValue } from 'lib/utils/responsiveUtils' export const useBreakpoint = (): number => { const { width } = useWindowSize() diff --git a/frontend/src/lib/hooks/useD3.ts b/frontend/src/lib/hooks/useD3.ts index 19de5fab0171c..8f1dcae4dbc02 100644 --- a/frontend/src/lib/hooks/useD3.ts +++ b/frontend/src/lib/hooks/useD3.ts @@ -1,5 +1,5 @@ -import { MutableRefObject, useEffect, useRef } from 'react' import * as d3 from 'd3' +import { MutableRefObject, useEffect, useRef } from 'react' export type D3Selector = d3.Selection export type D3Transition = d3.Transition diff --git a/frontend/src/lib/hooks/useKeyboardHotkeys.tsx b/frontend/src/lib/hooks/useKeyboardHotkeys.tsx index eb6bf3694132e..d08afd46441b2 100644 --- a/frontend/src/lib/hooks/useKeyboardHotkeys.tsx +++ b/frontend/src/lib/hooks/useKeyboardHotkeys.tsx @@ -1,5 +1,6 @@ import { useEventListener } from 'lib/hooks/useEventListener' import { DependencyList } from 'react' + import { HotKey } from '~/types' export interface HotkeyInterface { diff --git a/frontend/src/lib/hooks/useScrollable.ts b/frontend/src/lib/hooks/useScrollable.ts index 08fe12247725d..2d69b0df6905e 100644 --- a/frontend/src/lib/hooks/useScrollable.ts +++ b/frontend/src/lib/hooks/useScrollable.ts @@ -1,4 +1,5 @@ import { useLayoutEffect, useRef, useState } from 'react' + import { useResizeObserver } from './useResizeObserver' /** Determine whether an element is horizontally scrollable, on the left and on the right respectively. */ diff --git a/frontend/src/lib/hooks/useUploadFiles.ts b/frontend/src/lib/hooks/useUploadFiles.ts index 1b21c2613e3a5..ab4df041ba180 100644 --- a/frontend/src/lib/hooks/useUploadFiles.ts +++ b/frontend/src/lib/hooks/useUploadFiles.ts @@ -1,7 +1,8 @@ -import { MediaUploadResponse } from '~/types' import api from 'lib/api' import { useEffect, useState } from 'react' +import { MediaUploadResponse } from '~/types' + export const lazyImageBlobReducer = async (blob: Blob): Promise => { const blobReducer = (await import('image-blob-reduce')).default() return blobReducer.toBlob(blob, { max: 2000 }) diff --git a/frontend/src/lib/hooks/useWhyDidIRender.ts b/frontend/src/lib/hooks/useWhyDidIRender.ts index 2650dd3d7882d..eb8af54e66c63 100644 --- a/frontend/src/lib/hooks/useWhyDidIRender.ts +++ b/frontend/src/lib/hooks/useWhyDidIRender.ts @@ -1,4 +1,5 @@ import { useMemo, useRef } from 'react' + import { useFeatureFlag } from './useFeatureFlag' export function useWhyDidIRender(name: string, props: Record): void { diff --git a/frontend/src/lib/hooks/useWindowSize.js b/frontend/src/lib/hooks/useWindowSize.js index dc0615e5d9196..d9e137bc1b684 100644 --- a/frontend/src/lib/hooks/useWindowSize.js +++ b/frontend/src/lib/hooks/useWindowSize.js @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useEffect, useState } from 'react' export function useWindowSize() { const isClient = typeof window === 'object' diff --git a/frontend/src/lib/internalMetrics.ts b/frontend/src/lib/internalMetrics.ts index e137db77e41b8..9ae89f02c235d 100644 --- a/frontend/src/lib/internalMetrics.ts +++ b/frontend/src/lib/internalMetrics.ts @@ -1,5 +1,5 @@ -import posthog from 'posthog-js' import api, { getJSONOrThrow } from 'lib/api' +import posthog from 'posthog-js' import { getResponseBytes } from 'scenes/insights/utils' export interface TimeToSeeDataPayload { diff --git a/frontend/src/lib/introductions/GroupsIntroductionOption.tsx b/frontend/src/lib/introductions/GroupsIntroductionOption.tsx index c2b863cef3154..6050f89941867 100644 --- a/frontend/src/lib/introductions/GroupsIntroductionOption.tsx +++ b/frontend/src/lib/introductions/GroupsIntroductionOption.tsx @@ -1,8 +1,8 @@ import { useValues } from 'kea' -import Select from 'rc-select' -import { Link } from 'lib/lemon-ui/Link' import { groupsAccessLogic, GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' import { IconLock } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' +import Select from 'rc-select' // TODO: Remove, but de-ant FeatureFlagReleaseConditions first export function GroupsIntroductionOption({ value }: { value: any }): JSX.Element | null { diff --git a/frontend/src/lib/introductions/NewFeatureBanner.tsx b/frontend/src/lib/introductions/NewFeatureBanner.tsx index 6b8232e1d0000..da8b63c94028d 100644 --- a/frontend/src/lib/introductions/NewFeatureBanner.tsx +++ b/frontend/src/lib/introductions/NewFeatureBanner.tsx @@ -1,6 +1,6 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useValues } from 'kea' import { Link } from 'lib/lemon-ui/Link' -import { LemonButton } from '@posthog/lemon-ui' import { billingLogic } from 'scenes/billing/billingLogic' export function NewFeatureBanner(): JSX.Element | null { diff --git a/frontend/src/lib/introductions/groupsAccessLogic.ts b/frontend/src/lib/introductions/groupsAccessLogic.ts index 7e6b45edf201f..37bcb2e97972c 100644 --- a/frontend/src/lib/introductions/groupsAccessLogic.ts +++ b/frontend/src/lib/introductions/groupsAccessLogic.ts @@ -1,9 +1,10 @@ -import { kea, path, connect, selectors } from 'kea' -import { AvailableFeature } from '~/types' -import { teamLogic } from 'scenes/teamLogic' +import { connect, kea, path, selectors } from 'kea' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' +import { AvailableFeature } from '~/types' + import type { groupsAccessLogicType } from './groupsAccessLogicType' export enum GroupsAccessStatus { AlreadyUsing, diff --git a/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.tsx b/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.tsx index c9b5f8db5d554..394e9424ddd4a 100644 --- a/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.tsx +++ b/frontend/src/lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip.tsx @@ -1,9 +1,10 @@ +import './LemonActionableTooltip.scss' + import { Placement } from '@floating-ui/react' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { IconOpenInNew } from 'lib/lemon-ui/icons' -import { IconClose, IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' import { LemonButton } from '@posthog/lemon-ui' -import './LemonActionableTooltip.scss' +import { IconOpenInNew } from 'lib/lemon-ui/icons' +import { IconChevronLeft, IconChevronRight, IconClose } from 'lib/lemon-ui/icons' +import { Popover } from 'lib/lemon-ui/Popover/Popover' export type LemonActionableTooltipProps = { title?: string diff --git a/frontend/src/lib/lemon-ui/LemonActionableTooltip/index.ts b/frontend/src/lib/lemon-ui/LemonActionableTooltip/index.ts index 516fcb923abd2..4c3069d89979f 100644 --- a/frontend/src/lib/lemon-ui/LemonActionableTooltip/index.ts +++ b/frontend/src/lib/lemon-ui/LemonActionableTooltip/index.ts @@ -1,2 +1,2 @@ -export { LemonActionableTooltip } from './LemonActionableTooltip' export type { LemonActionableTooltipProps } from './LemonActionableTooltip' +export { LemonActionableTooltip } from './LemonActionableTooltip' diff --git a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.stories.tsx b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.stories.tsx index 20e40eae0b97c..04eb497c9e940 100644 --- a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonBadge } from './LemonBadge' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconPlusMini } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' + +import { LemonBadge } from './LemonBadge' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.tsx b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.tsx index 75f785f3429b8..cb5af2947a797 100644 --- a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.tsx +++ b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadge.tsx @@ -1,7 +1,8 @@ +import './LemonBadge.scss' + import clsx from 'clsx' import { compactNumber, humanFriendlyNumber } from 'lib/utils' import { CSSTransition } from 'react-transition-group' -import './LemonBadge.scss' interface LemonBadgePropsBase { size?: 'small' | 'medium' | 'large' diff --git a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadgeNumber.stories.tsx b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadgeNumber.stories.tsx index 09b2003aedbf2..92d2884d11a70 100644 --- a/frontend/src/lib/lemon-ui/LemonBadge/LemonBadgeNumber.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonBadge/LemonBadgeNumber.stories.tsx @@ -1,7 +1,8 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonBadge, LemonBadgeNumberProps } from './LemonBadge' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useState } from 'react' + +import { LemonBadge, LemonBadgeNumberProps } from './LemonBadge' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonBadge/index.ts b/frontend/src/lib/lemon-ui/LemonBadge/index.ts index 1c064e83a1404..26d5ac39abf75 100644 --- a/frontend/src/lib/lemon-ui/LemonBadge/index.ts +++ b/frontend/src/lib/lemon-ui/LemonBadge/index.ts @@ -1,2 +1,2 @@ -export { LemonBadge } from './LemonBadge' export type { LemonBadgeProps } from './LemonBadge' +export { LemonBadge } from './LemonBadge' diff --git a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss index e165aaa435d4f..9a948c4f24dd3 100644 --- a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss +++ b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.scss @@ -1,13 +1,14 @@ .LemonBanner { + align-items: center; border-radius: var(--radius); - padding: 0.5rem 0.75rem; + border: solid 1px var(--border-3000); color: var(--primary-alt); - font-weight: 500; display: flex; - align-items: center; - text-align: left; + font-weight: 500; gap: 0.5rem; min-height: 3rem; + padding: 0.5rem 0.75rem; + text-align: left; &.LemonBanner--info { background-color: var(--primary-alt-highlight); diff --git a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.stories.tsx b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.stories.tsx index c4ad6115a0433..4276607b0dc72 100644 --- a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { LemonBanner, LemonBannerProps } from './LemonBanner' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.tsx b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.tsx index 2f7f95cb06409..69e0da9f467af 100644 --- a/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.tsx +++ b/frontend/src/lib/lemon-ui/LemonBanner/LemonBanner.tsx @@ -1,9 +1,11 @@ import './LemonBanner.scss' -import { IconClose, IconInfo, IconWarning } from 'lib/lemon-ui/icons' + import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { IconClose, IconInfo, IconWarning } from 'lib/lemon-ui/icons' import { LemonButton, SideAction } from 'lib/lemon-ui/LemonButton' import { LemonButtonPropsBase } from 'lib/lemon-ui/LemonButton/LemonButton' -import { useActions, useValues } from 'kea' + import { lemonBannerLogic } from './lemonBannerLogic' export type LemonBannerAction = SideAction & Pick diff --git a/frontend/src/lib/lemon-ui/LemonBanner/index.ts b/frontend/src/lib/lemon-ui/LemonBanner/index.ts index a8b9f586f8000..f06472f32fe40 100644 --- a/frontend/src/lib/lemon-ui/LemonBanner/index.ts +++ b/frontend/src/lib/lemon-ui/LemonBanner/index.ts @@ -1,2 +1,2 @@ -export { LemonBanner } from './LemonBanner' export type { LemonBannerProps } from './LemonBanner' +export { LemonBanner } from './LemonBanner' diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss index 6422a901239ff..2a23964cfc0f9 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.scss @@ -1,24 +1,35 @@ -.LemonButton { - position: relative; - transition: background-color 200ms ease, color 200ms ease, border 200ms ease, opacity 200ms ease, - transform 100ms ease; - display: flex; - flex-direction: row; - flex-shrink: 0; +.LemonButton, +.Link.LemonButton { + // Make sure we override .Link's styles where needed, e.g. padding align-items: center; - justify-content: flex-start; - padding: 0.25rem 0.75rem; - gap: 0.5rem; + appearance: none !important; // Important as this gets overridden by Ant styles... background: none; border-radius: var(--radius); border: none; + cursor: pointer; + display: flex; + flex-direction: row; + + .posthog-3000 & { + font-family: var(--font-title); + } + + flex-shrink: 0; font-size: 0.875rem; - text-align: left; - line-height: 1.5rem; font-weight: 500; - cursor: pointer; + gap: 0.5rem; + justify-content: flex-start; + line-height: 1.5rem; + padding: 0.25rem 0.75rem; + position: relative; + text-align: left; + transition: background-color 200ms ease, color 200ms ease, border 200ms ease, opacity 200ms ease, + transform 100ms ease; user-select: none; - appearance: none !important; // Important as this gets overridden by Ant styles... + + .font-normal { + font-family: var(--font-sans); + } > span { display: flex; @@ -91,8 +102,7 @@ } } - &.LemonButton--small, - .Breadcrumbs3000 & { + &.LemonButton--small { gap: 0.25rem; > span { @@ -137,12 +147,6 @@ @each $status in ('primary', 'danger', 'primary-alt', 'muted') { &.LemonButton--status-#{$status} { - color: var(--#{$status}-3000, var(--#{$status}, var(--primary))); - - .LemonButton__icon { - color: var(--#{$status}-3000, var(--#{$status})); - } - // Primary - blocked color style &.LemonButton--primary { color: #fff; diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.stories.tsx b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.stories.tsx index c9c9c9c6251cc..03e34921b04b5 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.stories.tsx @@ -1,4 +1,13 @@ +import { Link } from '@posthog/lemon-ui' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import clsx from 'clsx' +import { useAsyncHandler } from 'lib/hooks/useAsyncHandler' +import { IconCalculate, IconInfo, IconPlus } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { capitalizeFirstLetter, delay, range } from 'lib/utils' +import { urls } from 'scenes/urls' + import { LemonButton, LemonButtonProps, @@ -6,15 +15,7 @@ import { LemonButtonWithDropdownProps, LemonButtonWithSideAction, } from './LemonButton' -import { IconCalculate, IconInfo, IconPlus } from 'lib/lemon-ui/icons' import { More } from './More' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { capitalizeFirstLetter, delay, range } from 'lib/utils' -import { urls } from 'scenes/urls' -import { Link } from '@posthog/lemon-ui' -import { useAsyncHandler } from 'lib/hooks/useAsyncHandler' -import clsx from 'clsx' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' const statuses: LemonButtonProps['status'][] = ['primary', 'danger', 'primary-alt', 'muted', 'stealth'] const types: LemonButtonProps['type'][] = ['primary', 'secondary', 'tertiary'] diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx index d52f2e601200e..087f10ea8558f 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton.tsx @@ -1,14 +1,16 @@ -import clsx from 'clsx' -import React, { useContext } from 'react' -import { IconArrowDropDown, IconChevronRight } from 'lib/lemon-ui/icons' -import { Link } from '../Link' -import { Spinner } from '../Spinner/Spinner' -import { Tooltip, TooltipProps } from '../Tooltip' import './LemonButton.scss' import './LemonButtonLegacy.scss' import './LemonButton3000.scss' + +import clsx from 'clsx' +import { IconArrowDropDown, IconChevronRight } from 'lib/lemon-ui/icons' +import React, { useContext } from 'react' + import { LemonDropdown, LemonDropdownProps } from '../LemonDropdown' +import { Link } from '../Link' import { PopoverReferenceContext } from '../Popover' +import { Spinner } from '../Spinner/Spinner' +import { Tooltip, TooltipProps } from '../Tooltip' export type LemonButtonDropdown = Omit @@ -109,6 +111,7 @@ export const LemonButton: React.FunctionComponent { const [popoverVisibility, popoverPlacement] = useContext(PopoverReferenceContext) || [false, null] + const within3000PageHeader = useContext(Within3000PageHeaderContext) if (!active && popoverVisibility) { active = true @@ -127,6 +130,9 @@ export const LemonButton: React.FunctionComponent disabled = true // Cannot interact with a loading button } + if (within3000PageHeader) { + size = 'small' + } let tooltipContent: TooltipProps['title'] if (disabledReason) { @@ -184,7 +190,7 @@ export const LemonButton: React.FunctionComponent - + {icon ? {icon} : null} {children ? {children} : null} {sideIcon ? {sideIcon} : null} @@ -210,6 +216,8 @@ export const LemonButton: React.FunctionComponent(false) + export type SideAction = Pick< LemonButtonProps, 'onClick' | 'to' | 'disabled' | 'icon' | 'type' | 'tooltip' | 'data-attr' | 'aria-label' | 'status' | 'targetBlank' diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss b/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss index e0067bc5406af..70fa6ad667194 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButton3000.scss @@ -1,11 +1,12 @@ -.posthog-3000 { +.posthog-3000.posthog-3000.posthog-3000 { + // The repetition is a specificity hack, so that we override .LemonButton --transition: opacity 200ms ease, transform 200ms ease; .LemonButton { border-width: 0; border-style: solid; border-color: transparent; - min-height: 2em; + min-height: 2.125rem; padding: 0; position: relative; outline: none; @@ -13,7 +14,7 @@ border-radius: 6px; cursor: pointer; - > span { + .LemonButton__chrome { border-radius: 6px; font-size: 0.875rem; display: flex; @@ -22,68 +23,70 @@ align-items: center; justify-content: flex-start; background: none; - border-width: 1px; border-style: solid; border-color: transparent; font-weight: 500; gap: 0.5rem; line-height: 1.5rem; - min-height: 2em; + min-height: 2.125rem; position: relative; text-align: left; transition: var(--transition); padding: 0.25rem 0.75rem; width: 100%; - .LemonButton__icon:first-child { - transition: var(--transition); - color: var(--default); + .LemonButton__icon { opacity: 0.5; } } + &.LemonButton--full-width { + .LemonButton__chrome { + padding-left: 0.5rem; + padding-right: 0.5rem; + } + } + &.LemonButton--xsmall { min-height: 1.5rem; padding-left: 0; + font-size: 0.8125rem; - > span { + .LemonButton__chrome { min-height: 1.5rem; padding: 0.125rem 0.375rem; } - &.LemonButton--has-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { - > span { + &.LemonButton--has-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + .LemonButton__chrome { padding-left: 0.25rem; } } - &.LemonButton--has-side-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { - > span { + &.LemonButton--has-side-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + .LemonButton__chrome { padding-right: 0.25rem; } } } &.LemonButton--small { - min-height: 1.8rem; + min-height: 1.75rem; + font-size: 0.8125rem; - > span { - min-height: 1.8rem; + .LemonButton__chrome { + min-height: 1.75rem; padding: 0.25rem 0.5rem; } - &.LemonButton--has-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { - > span { + &.LemonButton--has-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + .LemonButton__chrome { padding-left: 0.375rem; } } - &.LemonButton--has-side-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { - > span { + &.LemonButton--has-side-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { + .LemonButton__chrome { padding-right: 0.375rem; } } @@ -92,26 +95,24 @@ &.LemonButton--large { min-height: 2.5rem; - > span { + .LemonButton__chrome { gap: 0.75rem; min-height: 2.5rem; padding: 0.5rem 1rem; } - &.LemonButton--has-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { + &.LemonButton--has-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { padding-left: 0; - > span { + .LemonButton__chrome { padding-left: 0.75rem; } } - &.LemonButton--has-side-icon:not(.LemonButton--no-padding), - &.LemonButton--no-content:not(.LemonButton--no-padding) { + &.LemonButton--has-side-icon:not(.LemonButton--no-content, .LemonButton--no-padding) { padding-right: 0; - > span { + .LemonButton__chrome { padding-right: 0.75rem; } } @@ -121,7 +122,7 @@ padding: 0; min-height: 0; - > span { + .LemonButton__chrome { padding: 0; min-height: 0; } @@ -131,20 +132,21 @@ border-width: 1px; padding-bottom: 1px; - > span { + .LemonButton__chrome { + border-width: 1px; margin: 0 -1px; top: -1px; } &:not([aria-disabled='true']):hover, &.LemonButton--active { - > span { + .LemonButton__chrome { top: -1.5px; } } &:not([aria-disabled='true']):active { - > span { + .LemonButton__chrome { top: -0.5px; } } @@ -154,7 +156,7 @@ background: var(--primary-3000-frame-bg); border-color: var(--primary-3000-frame-border); - > span { + .LemonButton__chrome { background: var(--primary-3000-button-bg); border-color: var(--primary-3000-button-border); color: #111; @@ -171,18 +173,18 @@ background: var(--secondary-3000-frame-bg); border-color: var(--secondary-3000-frame-border); - &:not([aria-disabled='true']):hover > span { + &:not([aria-disabled='true']):hover .LemonButton__chrome { border-color: var(--secondary-3000-button-border-hover); } - > span { + .LemonButton__chrome { color: var(--default); background: var(--accent-3000); border-color: var(--secondary-3000-button-border); } &.LemonButton--active { - > span { + .LemonButton__chrome { color: var(--default); background: var(--bg-light); border-color: var(--secondary-3000-button-border-hover); @@ -192,7 +194,7 @@ &.LemonButton--is-stealth:not(.LemonButton--active) { &:hover { - > span { + .LemonButton__chrome { border-color: var(--secondary-3000-button-border); } } @@ -201,7 +203,7 @@ background-color: transparent; border-color: transparent; - > span { + .LemonButton__chrome { background-color: transparent; border-color: transparent; color: var(--muted); @@ -244,19 +246,19 @@ border-top-right-radius: 5px; border-bottom-right-radius: 5px; - & .LemonButton { + .LemonButton { background: none !important; border: none !important; padding-bottom: 0 !important; margin: 0 auto !important; height: 100%; + } - > span { - margin: auto !important; - top: 0 !important; - background: none !important; - border: none !important; - } + .LemonButton__chrome { + margin: auto !important; + top: 0 !important; + background: none !important; + border: none !important; } &:not([aria-disabled='true']):hover { diff --git a/frontend/src/lib/lemon-ui/LemonButton/LemonButtonLegacy.scss b/frontend/src/lib/lemon-ui/LemonButton/LemonButtonLegacy.scss index fc10519414fee..abe0e6cc3e802 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/LemonButtonLegacy.scss +++ b/frontend/src/lib/lemon-ui/LemonButton/LemonButtonLegacy.scss @@ -124,6 +124,12 @@ body:not(.posthog-3000) { background: var(--#{$status}-highlight, var(--primary-highlight)); } + color: var(--#{$status}-3000, var(--#{$status}, var(--primary))); + + .LemonButton__icon { + color: var(--#{$status}-3000, var(--#{$status})); + } + &:not([aria-disabled='true']):active { color: var(--#{$status}-dark, var(--primary-dark)); @@ -134,6 +140,8 @@ body:not(.posthog-3000) { // Primary - blocked color style &.LemonButton--primary { + color: #fff; + .LemonButton__icon { color: #fff; } diff --git a/frontend/src/lib/lemon-ui/LemonButton/More.tsx b/frontend/src/lib/lemon-ui/LemonButton/More.tsx index 00f03c2879ec3..71f6943ef93ee 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/More.tsx +++ b/frontend/src/lib/lemon-ui/LemonButton/More.tsx @@ -1,6 +1,7 @@ -import { LemonButtonWithDropdown } from '.' import { IconEllipsis } from 'lib/lemon-ui/icons' + import { PopoverProps } from '../Popover/Popover' +import { LemonButtonWithDropdown } from '.' import { LemonButtonProps, LemonButtonWithDropdownProps } from './LemonButton' export type MoreProps = Partial> & diff --git a/frontend/src/lib/lemon-ui/LemonButton/index.ts b/frontend/src/lib/lemon-ui/LemonButton/index.ts index 944bbdc1a3c75..1f679448b3949 100644 --- a/frontend/src/lib/lemon-ui/LemonButton/index.ts +++ b/frontend/src/lib/lemon-ui/LemonButton/index.ts @@ -1,8 +1,8 @@ -export { LemonButton, LemonButtonWithSideAction, LemonButtonWithDropdown } from './LemonButton' export type { - LemonButtonPropsBase, LemonButtonProps, - LemonButtonWithSideActionProps, + LemonButtonPropsBase, LemonButtonWithDropdownProps, + LemonButtonWithSideActionProps, SideAction, } from './LemonButton' +export { LemonButton, LemonButtonWithDropdown, LemonButtonWithSideAction } from './LemonButton' diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.stories.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.stories.tsx index 4d11003196266..7de56eab1a06b 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonCalendar, LemonCalendarProps } from './LemonCalendar' import { dayjs } from 'lib/dayjs' +import { LemonCalendar, LemonCalendarProps } from './LemonCalendar' + type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Lemon Calendar/Lemon Calendar', diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.test.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.test.tsx index fb3ca56a7852f..ea52b8c681552 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.test.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.test.tsx @@ -1,9 +1,11 @@ -import { LemonCalendar } from './LemonCalendar' import { render, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { getAllByDataAttr, getByDataAttr } from '~/test/byDataAttr' import { dayjs } from 'lib/dayjs' +import { getAllByDataAttr, getByDataAttr } from '~/test/byDataAttr' + +import { LemonCalendar } from './LemonCalendar' + describe('LemonCalendar', () => { test('click and move between months with one month showing', async () => { const onLeftmostMonthChanged = jest.fn() diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.tsx index 25fcc5c4cfecc..b9ca6dd658ab5 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendar.tsx @@ -1,11 +1,12 @@ import './LemonCalendar.scss' -import { useEffect, useState } from 'react' -import { dayjs } from 'lib/dayjs' -import { range } from 'lib/utils' -import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' + import clsx from 'clsx' import { useValues } from 'kea' +import { dayjs } from 'lib/dayjs' +import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' +import { range } from 'lib/utils' +import { useEffect, useState } from 'react' import { teamLogic } from 'scenes/teamLogic' export interface LemonCalendarProps { diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.stories.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.stories.tsx index a27e3f21ff22d..1c6bff250dd2b 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.stories.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { dayjs } from 'lib/dayjs' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonCalendarSelect, LemonCalendarSelectProps } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { dayjs } from 'lib/dayjs' import { formatDate } from 'lib/utils' +import { useState } from 'react' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.test.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.test.tsx index 6d2850a258ced..dd44efa6634cc 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.test.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.test.tsx @@ -1,9 +1,10 @@ -import { useState } from 'react' import { render, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { getByDataAttr } from '~/test/byDataAttr' -import { LemonCalendarSelect } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' import { dayjs } from 'lib/dayjs' +import { LemonCalendarSelect } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' +import { useState } from 'react' + +import { getByDataAttr } from '~/test/byDataAttr' describe('LemonCalendarSelect', () => { test('select various dates', async () => { diff --git a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx index d06f263ab8dc0..754b42c8ffc4c 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendar/LemonCalendarSelect.tsx @@ -1,8 +1,9 @@ -import { LemonCalendar } from 'lib/lemon-ui/LemonCalendar/LemonCalendar' -import { useState } from 'react' import { dayjs } from 'lib/dayjs' -import { LemonButton, LemonButtonProps, LemonButtonWithSideAction, SideAction } from 'lib/lemon-ui/LemonButton' import { IconClose } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps, LemonButtonWithSideAction, SideAction } from 'lib/lemon-ui/LemonButton' +import { LemonCalendar } from 'lib/lemon-ui/LemonCalendar/LemonCalendar' +import { useState } from 'react' + import { Popover } from '../Popover' export interface LemonCalendarSelectProps { diff --git a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.stories.tsx b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.stories.tsx index ec59718296c29..d11471a3db00c 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.stories.tsx @@ -1,10 +1,10 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { dayjs } from 'lib/dayjs' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonCalendarRange, LemonCalendarRangeProps } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { dayjs } from 'lib/dayjs' import { formatDateRange } from 'lib/utils' +import { useState } from 'react' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.test.tsx b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.test.tsx index 9dff31017de88..82546dfb305f5 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.test.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.test.tsx @@ -1,9 +1,10 @@ -import { useState } from 'react' import { render, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' -import { getByDataAttr } from '~/test/byDataAttr' -import { LemonCalendarRange } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' import { dayjs } from 'lib/dayjs' +import { LemonCalendarRange } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' +import { useState } from 'react' + +import { getByDataAttr } from '~/test/byDataAttr' describe('LemonCalendarRange', () => { test('select various ranges', async () => { diff --git a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.tsx b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.tsx index 10f968ea2ba28..92492cc2bfe50 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRange.tsx @@ -1,8 +1,9 @@ -import { useState } from 'react' import { dayjs } from 'lib/dayjs' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconClose } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { formatDate, formatDateRange } from 'lib/utils' +import { useState } from 'react' + import { LemonCalendarRangeInline } from './LemonCalendarRangeInline' export interface LemonCalendarRangeProps { diff --git a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.stories.tsx b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.stories.tsx index 1cc8d6dec12a0..fbdbc8a174470 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.stories.tsx @@ -1,8 +1,9 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonCalendarRangeProps } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' import { dayjs } from 'lib/dayjs' +import { LemonCalendarRangeProps } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' import { formatDateRange } from 'lib/utils' +import { useState } from 'react' + import { LemonCalendarRangeInline } from './LemonCalendarRangeInline' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.tsx b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.tsx index 898f77728dd26..3fb703a5b4081 100644 --- a/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.tsx +++ b/frontend/src/lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline.tsx @@ -1,7 +1,8 @@ +import clsx from 'clsx' +import { dayjs } from 'lib/dayjs' import { LemonCalendar } from 'lib/lemon-ui/LemonCalendar/LemonCalendar' import { useEffect, useState } from 'react' -import { dayjs } from 'lib/dayjs' -import clsx from 'clsx' + import { LemonCalendarRangeProps } from './LemonCalendarRange' /** Used to calculate how many calendars fit on the screen */ diff --git a/frontend/src/lib/lemon-ui/LemonCard/index.ts b/frontend/src/lib/lemon-ui/LemonCard/index.ts index b3d760635a508..1bad4093b23d8 100644 --- a/frontend/src/lib/lemon-ui/LemonCard/index.ts +++ b/frontend/src/lib/lemon-ui/LemonCard/index.ts @@ -1,2 +1,2 @@ -export { LemonCard } from './LemonCard' export type { LemonCardProps } from './LemonCard' +export { LemonCard } from './LemonCard' diff --git a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.stories.tsx b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.stories.tsx index 370c0e17ed1d1..c26bc8a52f2a2 100644 --- a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { LemonCheckbox, LemonCheckboxProps } from './LemonCheckbox' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.tsx b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.tsx index 810d5969826f6..29fc08835695d 100644 --- a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.tsx +++ b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.tsx @@ -1,6 +1,8 @@ +import './LemonCheckbox.scss' + import clsx from 'clsx' import { useEffect, useMemo, useState } from 'react' -import './LemonCheckbox.scss' + import { Tooltip } from '../Tooltip' export interface LemonCheckboxProps { diff --git a/frontend/src/lib/lemon-ui/LemonCheckbox/index.ts b/frontend/src/lib/lemon-ui/LemonCheckbox/index.ts index 0859c338959ff..d9e9746415f55 100644 --- a/frontend/src/lib/lemon-ui/LemonCheckbox/index.ts +++ b/frontend/src/lib/lemon-ui/LemonCheckbox/index.ts @@ -1,2 +1,2 @@ -export { LemonCheckbox } from './LemonCheckbox' export type { LemonCheckboxProps } from './LemonCheckbox' +export { LemonCheckbox } from './LemonCheckbox' diff --git a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.stories.tsx b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.stories.tsx index 253d295bf135c..4eaefe6b2a8cc 100644 --- a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { LemonCollapse as LemonCollapseComponent } from './LemonCollapse' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx index 07a04ab3b0751..378ad687033c9 100644 --- a/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx +++ b/frontend/src/lib/lemon-ui/LemonCollapse/LemonCollapse.tsx @@ -1,11 +1,13 @@ +import './LemonCollapse.scss' + import clsx from 'clsx' import React, { ReactNode, useState } from 'react' import { Transition } from 'react-transition-group' import { ENTERED, ENTERING } from 'react-transition-group/Transition' import useResizeObserver from 'use-resize-observer' + import { IconUnfoldLess, IconUnfoldMore } from '../icons' import { LemonButton } from '../LemonButton' -import './LemonCollapse.scss' export interface LemonCollapsePanel { key: K diff --git a/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.stories.tsx b/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.stories.tsx index 3d1ac6ae829de..83b75dbacee26 100644 --- a/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.stories.tsx @@ -1,7 +1,8 @@ +import { Link } from '@posthog/lemon-ui' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonDialog, LemonDialogProps } from './LemonDialog' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { Link } from '@posthog/lemon-ui' + +import { LemonDialog, LemonDialogProps } from './LemonDialog' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.tsx b/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.tsx index 8ef22e6628dd6..f7ddd96fbc40a 100644 --- a/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.tsx +++ b/frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.tsx @@ -1,9 +1,9 @@ -import { ReactNode, useEffect, useRef, useState } from 'react' +import { useValues } from 'kea' +import { router } from 'kea-router' import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' import { LemonModal, LemonModalProps } from 'lib/lemon-ui/LemonModal' +import { ReactNode, useEffect, useRef, useState } from 'react' import { createRoot } from 'react-dom/client' -import { useValues } from 'kea' -import { router } from 'kea-router' export type LemonDialogProps = Pick & { primaryButton?: LemonButtonProps | null diff --git a/frontend/src/lib/lemon-ui/LemonDialog/index.ts b/frontend/src/lib/lemon-ui/LemonDialog/index.ts index e091a9cc0f90f..92240b21e97f8 100644 --- a/frontend/src/lib/lemon-ui/LemonDialog/index.ts +++ b/frontend/src/lib/lemon-ui/LemonDialog/index.ts @@ -1,2 +1,2 @@ -export { LemonDialog } from './LemonDialog' export type { LemonDialogProps } from './LemonDialog' +export { LemonDialog } from './LemonDialog' diff --git a/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.stories.tsx b/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.stories.tsx index 5680846242126..54bb7be44dbf8 100644 --- a/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.stories.tsx @@ -1,9 +1,10 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonDivider, LemonDividerProps } from './LemonDivider' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonRow } from 'lib/lemon-ui/LemonRow' + import { Lettermark, LettermarkColor } from '../Lettermark/Lettermark' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { ProfileBubbles } from '../ProfilePicture' +import { LemonDivider, LemonDividerProps } from './LemonDivider' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.tsx b/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.tsx index 557d4ed70156e..c6051406f9016 100644 --- a/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.tsx +++ b/frontend/src/lib/lemon-ui/LemonDivider/LemonDivider.tsx @@ -1,6 +1,7 @@ -import clsx from 'clsx' import './LemonDivider.scss' +import clsx from 'clsx' + export interface LemonDividerProps { /** 3x the thickness of the line. */ thick?: boolean diff --git a/frontend/src/lib/lemon-ui/LemonDivider/index.ts b/frontend/src/lib/lemon-ui/LemonDivider/index.ts index dc8fe94a055f7..9370254049c75 100644 --- a/frontend/src/lib/lemon-ui/LemonDivider/index.ts +++ b/frontend/src/lib/lemon-ui/LemonDivider/index.ts @@ -1,2 +1,2 @@ -export { LemonDivider } from './LemonDivider' export type { LemonDividerProps } from './LemonDivider' +export { LemonDivider } from './LemonDivider' diff --git a/frontend/src/lib/lemon-ui/LemonDropdown/LemonDropdown.tsx b/frontend/src/lib/lemon-ui/LemonDropdown/LemonDropdown.tsx index 35588606fcdd9..c1fe28245c5c6 100644 --- a/frontend/src/lib/lemon-ui/LemonDropdown/LemonDropdown.tsx +++ b/frontend/src/lib/lemon-ui/LemonDropdown/LemonDropdown.tsx @@ -1,4 +1,5 @@ import React, { MouseEventHandler, useContext, useEffect, useRef, useState } from 'react' + import { Popover, PopoverOverlayContext, PopoverProps } from '../Popover' export interface LemonDropdownProps extends Omit { diff --git a/frontend/src/lib/lemon-ui/LemonDropdown/index.ts b/frontend/src/lib/lemon-ui/LemonDropdown/index.ts index c60bc106452cc..9a0e2a2dca3b5 100644 --- a/frontend/src/lib/lemon-ui/LemonDropdown/index.ts +++ b/frontend/src/lib/lemon-ui/LemonDropdown/index.ts @@ -1,2 +1,2 @@ -export { LemonDropdown } from './LemonDropdown' export type { LemonDropdownProps } from './LemonDropdown' +export { LemonDropdown } from './LemonDropdown' diff --git a/frontend/src/lib/lemon-ui/LemonFileInput/LemonFileInput.tsx b/frontend/src/lib/lemon-ui/LemonFileInput/LemonFileInput.tsx index 25cf4daf6e04d..f12cc67d0eb1f 100644 --- a/frontend/src/lib/lemon-ui/LemonFileInput/LemonFileInput.tsx +++ b/frontend/src/lib/lemon-ui/LemonFileInput/LemonFileInput.tsx @@ -1,9 +1,10 @@ -import { ChangeEvent, createRef, RefObject, useEffect, useState } from 'react' +import './LemonFileInput.scss' + +import clsx from 'clsx' import { IconUploadFile } from 'lib/lemon-ui/icons' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import clsx from 'clsx' -import './LemonFileInput.scss' +import { ChangeEvent, createRef, RefObject, useEffect, useState } from 'react' export interface LemonFileInputProps extends Pick { value?: File[] diff --git a/frontend/src/lib/lemon-ui/LemonFileInput/index.ts b/frontend/src/lib/lemon-ui/LemonFileInput/index.ts index a6b3cbff76d30..606370c2b67d4 100644 --- a/frontend/src/lib/lemon-ui/LemonFileInput/index.ts +++ b/frontend/src/lib/lemon-ui/LemonFileInput/index.ts @@ -1,2 +1,2 @@ -export { LemonFileInput } from './LemonFileInput' export type { LemonFileInputProps } from './LemonFileInput' +export { LemonFileInput } from './LemonFileInput' diff --git a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss index c00645bafc1e1..a5f81f7376b58 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss +++ b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.scss @@ -17,10 +17,18 @@ &:hover:not([aria-disabled='true']) { border-color: var(--primary-3000-hover); + + .posthog-3000 & { + border-color: var(--border-bold); + } } &.LemonInput--focused:not([aria-disabled='true']) { border-color: var(--primary-3000); + + .posthog-3000 & { + border-color: var(--border-bold); + } } &.LemonInput--transparent-background { diff --git a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx index 53e20b45848de..e254c18262fcb 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.stories.tsx @@ -1,9 +1,9 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' - -import { LemonInput } from './LemonInput' import { IconArrowDropDown, IconCalendar } from 'lib/lemon-ui/icons' import { LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' +import { useState } from 'react' + +import { LemonInput } from './LemonInput' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx index 650d621528ea2..5149610d9cc28 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx +++ b/frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx @@ -1,8 +1,9 @@ import './LemonInput.scss' -import React, { useRef, useState } from 'react' + import clsx from 'clsx' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconClose, IconEyeHidden, IconEyeVisible, IconMagnifier } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import React, { useRef, useState } from 'react' interface LemonInputPropsBase extends Pick< diff --git a/frontend/src/lib/lemon-ui/LemonInput/index.ts b/frontend/src/lib/lemon-ui/LemonInput/index.ts index 06f396399f111..43895c91420c9 100644 --- a/frontend/src/lib/lemon-ui/LemonInput/index.ts +++ b/frontend/src/lib/lemon-ui/LemonInput/index.ts @@ -1,2 +1,2 @@ -export { LemonInput } from './LemonInput' export type { LemonInputProps, LemonInputPropsNumber, LemonInputPropsText } from './LemonInput' +export { LemonInput } from './LemonInput' diff --git a/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.stories.tsx b/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.stories.tsx index cb81dd236aeed..0a3a1ecf512da 100644 --- a/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.stories.tsx @@ -1,7 +1,8 @@ -import { useState } from 'react' +import { LemonModal } from '@posthog/lemon-ui' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { useState } from 'react' + import { LemonLabel, LemonLabelProps } from './LemonLabel' -import { LemonModal } from '@posthog/lemon-ui' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.tsx b/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.tsx index 7568d9b838eed..8aeb58c93d545 100644 --- a/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.tsx +++ b/frontend/src/lib/lemon-ui/LemonLabel/LemonLabel.tsx @@ -1,8 +1,10 @@ import './LemonLabel.scss' -import { Tooltip } from '../Tooltip' -import { IconInfo } from 'lib/lemon-ui/icons' + import clsx from 'clsx' +import { IconInfo } from 'lib/lemon-ui/icons' + import { Link, LinkProps } from '../Link' +import { Tooltip } from '../Tooltip' export interface LemonLabelProps extends Pick, 'id' | 'htmlFor' | 'form' | 'children' | 'className'> { diff --git a/frontend/src/lib/lemon-ui/LemonLabel/index.ts b/frontend/src/lib/lemon-ui/LemonLabel/index.ts index 508b15abac24d..eb65b794dc459 100644 --- a/frontend/src/lib/lemon-ui/LemonLabel/index.ts +++ b/frontend/src/lib/lemon-ui/LemonLabel/index.ts @@ -1,2 +1,2 @@ -export { LemonLabel } from './LemonLabel' export type { LemonLabelProps } from './LemonLabel' +export { LemonLabel } from './LemonLabel' diff --git a/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.stories.tsx b/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.stories.tsx index 89c4b786360e6..30065bcc45fc6 100644 --- a/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { LemonMarkdown as LemonMarkdownComponent, LemonMarkdownProps } from './LemonMarkdown' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.tsx b/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.tsx index 90d8258c1cf30..118f182c52b5c 100644 --- a/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.tsx +++ b/frontend/src/lib/lemon-ui/LemonMarkdown/LemonMarkdown.tsx @@ -1,8 +1,10 @@ -import ReactMarkdown from 'react-markdown' import './LemonMarkdown.scss' -import { Link } from '../Link' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import clsx from 'clsx' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' +import ReactMarkdown from 'react-markdown' + +import { Link } from '../Link' export interface LemonMarkdownProps { children: string diff --git a/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.stories.tsx b/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.stories.tsx index 05ed74c6eea29..90ed1a63e0d5a 100644 --- a/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.stories.tsx @@ -1,11 +1,12 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { Splotch, SplotchColor } from '../Splotch' import { + LemonMenuItems, LemonMenuOverlay as LemonMenuOverlayComponent, LemonMenuOverlayProps, - LemonMenuItems, LemonMenuSection, } from './LemonMenu' -import { Splotch, SplotchColor } from '../Splotch' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.tsx b/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.tsx index b061b90d56c35..227a2c3d6a342 100644 --- a/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.tsx +++ b/frontend/src/lib/lemon-ui/LemonMenu/LemonMenu.tsx @@ -1,13 +1,15 @@ +import { useValues } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import React, { FunctionComponent, ReactNode, useCallback, useMemo } from 'react' + +import { KeyboardShortcut, KeyboardShortcutProps } from '~/layout/navigation-3000/components/KeyboardShortcut' + import { LemonButton, LemonButtonProps } from '../LemonButton' -import { TooltipProps } from '../Tooltip' import { LemonDivider } from '../LemonDivider' import { LemonDropdown, LemonDropdownProps } from '../LemonDropdown' +import { TooltipProps } from '../Tooltip' import { useKeyboardNavigation } from './useKeyboardNavigation' -import { useValues } from 'kea' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { KeyboardShortcut, KeyboardShortcutProps } from '~/layout/navigation-3000/components/KeyboardShortcut' type KeyboardShortcut = Array diff --git a/frontend/src/lib/lemon-ui/LemonMenu/index.ts b/frontend/src/lib/lemon-ui/LemonMenu/index.ts index 38661b24bc2fb..c12d3404d74e6 100644 --- a/frontend/src/lib/lemon-ui/LemonMenu/index.ts +++ b/frontend/src/lib/lemon-ui/LemonMenu/index.ts @@ -1,2 +1,2 @@ +export type { LemonMenuItem, LemonMenuItems, LemonMenuSection } from './LemonMenu' export { LemonMenu } from './LemonMenu' -export type { LemonMenuItem, LemonMenuSection, LemonMenuItems } from './LemonMenu' diff --git a/frontend/src/lib/lemon-ui/LemonModal/LemonModal.stories.tsx b/frontend/src/lib/lemon-ui/LemonModal/LemonModal.stories.tsx index 55ca9533948b9..50de36a8d50bd 100644 --- a/frontend/src/lib/lemon-ui/LemonModal/LemonModal.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonModal/LemonModal.stories.tsx @@ -1,7 +1,8 @@ -import { useState } from 'react' import { Meta, StoryFn } from '@storybook/react' -import { LemonModal, LemonModalProps } from './LemonModal' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useState } from 'react' + +import { LemonModal, LemonModalProps } from './LemonModal' const meta: Meta = { title: 'Lemon UI/Lemon Modal', diff --git a/frontend/src/lib/lemon-ui/LemonModal/LemonModal.tsx b/frontend/src/lib/lemon-ui/LemonModal/LemonModal.tsx index 76dde577a77f9..b791f1b4b9826 100644 --- a/frontend/src/lib/lemon-ui/LemonModal/LemonModal.tsx +++ b/frontend/src/lib/lemon-ui/LemonModal/LemonModal.tsx @@ -1,14 +1,15 @@ -import { useEffect, useRef, useState } from 'react' -import clsx from 'clsx' -import Modal from 'react-modal' +import './LemonModal.scss' +import clsx from 'clsx' import { IconClose } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useEffect, useRef, useState } from 'react' +import Modal from 'react-modal' -import './LemonModal.scss' -import { Tooltip } from '../Tooltip' import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' +import { Tooltip } from '../Tooltip' + interface LemonModalInnerProps { children?: React.ReactNode className?: string diff --git a/frontend/src/lib/lemon-ui/LemonModal/index.ts b/frontend/src/lib/lemon-ui/LemonModal/index.ts index 02ed8923ad50a..3f8910e2634e7 100644 --- a/frontend/src/lib/lemon-ui/LemonModal/index.ts +++ b/frontend/src/lib/lemon-ui/LemonModal/index.ts @@ -1,2 +1,2 @@ -export { LemonModal } from './LemonModal' export type { LemonModalProps } from './LemonModal' +export { LemonModal } from './LemonModal' diff --git a/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.stories.tsx b/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.stories.tsx index 7fc77ead26486..7215a14b0ea11 100644 --- a/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.stories.tsx @@ -1,9 +1,10 @@ +import { IconGear } from '@posthog/icons' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonProgressCircle, LemonProgressCircleProps } from './LemonProgressCircle' import { useEffect, useState } from 'react' + import { LemonButton } from '../LemonButton' -import { IconGear } from '@posthog/icons' import { LemonCheckbox } from '../LemonCheckbox' +import { LemonProgressCircle, LemonProgressCircleProps } from './LemonProgressCircle' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.tsx b/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.tsx index 6d8e48419aa19..87eb0959a1450 100644 --- a/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.tsx +++ b/frontend/src/lib/lemon-ui/LemonProgressCircle/LemonProgressCircle.tsx @@ -1,6 +1,7 @@ -import clsx from 'clsx' import './LemonProgressCircle.scss' +import clsx from 'clsx' + export type LemonProgressCircleProps = { strokePercentage?: number backgroundStrokeOpacity?: number diff --git a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.stories.tsx b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.stories.tsx index c67cb1759c0a8..bb58867af0373 100644 --- a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { IconInfo, IconPremium } from 'lib/lemon-ui/icons' + import { LemonRow, LemonRowProps } from './LemonRow' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.tsx b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.tsx index b41c2397c3550..c109e2d818a63 100644 --- a/frontend/src/lib/lemon-ui/LemonRow/LemonRow.tsx +++ b/frontend/src/lib/lemon-ui/LemonRow/LemonRow.tsx @@ -1,9 +1,11 @@ -import clsx from 'clsx' import './LemonRow.scss' -import { Tooltip } from '../Tooltip' -import { Spinner } from '../Spinner/Spinner' + +import clsx from 'clsx' import React from 'react' +import { Spinner } from '../Spinner/Spinner' +import { Tooltip } from '../Tooltip' + // Fix for function type inference in forwardRef, so that function components wrapped with forwardRef can be generic. // For some reason the @types/react definitons as React 16 and TS 4.9 don't work, because `P` (the props) is wrapped in // `Pick` (inside `React.PropsWithoutRef`), which breaks TypeScript's ability to reason about it as a generic type. diff --git a/frontend/src/lib/lemon-ui/LemonRow/index.ts b/frontend/src/lib/lemon-ui/LemonRow/index.ts index 9e5c1b25d9e48..b2b3718d558b4 100644 --- a/frontend/src/lib/lemon-ui/LemonRow/index.ts +++ b/frontend/src/lib/lemon-ui/LemonRow/index.ts @@ -1,2 +1,2 @@ -export { LemonRow } from './LemonRow' export type { LemonRowProps, LemonRowPropsBase } from './LemonRow' +export { LemonRow } from './LemonRow' diff --git a/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.stories.tsx b/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.stories.tsx index 34d31b37b84c7..0b0090ecbfde9 100644 --- a/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useState } from 'react' + import { IconCalculate, IconCalendar, IconLightBulb, IconSettings } from '../icons' import { LemonSegmentedButton, LemonSegmentedButtonOption, LemonSegmentedButtonProps } from './LemonSegmentedButton' diff --git a/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.tsx b/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.tsx index 69f4bea414371..2e8e6ef391975 100644 --- a/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.tsx +++ b/frontend/src/lib/lemon-ui/LemonSegmentedButton/LemonSegmentedButton.tsx @@ -1,11 +1,13 @@ -import clsx from 'clsx' -import React from 'react' -import { LemonButton, LemonButtonProps } from '../LemonButton' -import { useSliderPositioning } from '../hooks' import './LemonSegmentedButton.scss' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' + +import clsx from 'clsx' import { useValues } from 'kea' import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import React from 'react' + +import { useSliderPositioning } from '../hooks' +import { LemonButton, LemonButtonProps } from '../LemonButton' export interface LemonSegmentedButtonOption { value: T diff --git a/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.stories.tsx b/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.stories.tsx index f86c3b9fed7b0..6b78f42d26663 100644 --- a/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonSelect, LemonSelectOptions, LemonSelectProps } from './LemonSelect' import { capitalizeFirstLetter } from 'lib/utils' +import { LemonSelect, LemonSelectOptions, LemonSelectProps } from './LemonSelect' + type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Lemon Select', diff --git a/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.tsx b/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.tsx index 51093dcb54c11..d57dc883eb34f 100644 --- a/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.tsx +++ b/frontend/src/lib/lemon-ui/LemonSelect/LemonSelect.tsx @@ -1,11 +1,12 @@ +import './LemonSelect.scss' + +import clsx from 'clsx' import React, { useMemo } from 'react' + import { IconClose } from '../icons' import { LemonButton, LemonButtonProps } from '../LemonButton' -import { PopoverProps } from '../Popover' -import './LemonSelect.scss' -import clsx from 'clsx' -import { TooltipProps } from '../Tooltip' import { + isLemonMenuSection, LemonMenu, LemonMenuItem, LemonMenuItemBase, @@ -13,8 +14,9 @@ import { LemonMenuItemNode, LemonMenuProps, LemonMenuSection, - isLemonMenuSection, } from '../LemonMenu/LemonMenu' +import { PopoverProps } from '../Popover' +import { TooltipProps } from '../Tooltip' // Select options are basically menu items that handle onClick and active state internally interface LemonSelectOptionBase extends Omit { diff --git a/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.stories.tsx b/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.stories.tsx index 049a85f9bfe0e..c360b9f86e796 100644 --- a/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.stories.tsx @@ -1,8 +1,9 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonSelectMultiple, LemonSelectMultipleProps } from './LemonSelectMultiple' -import { ProfilePicture } from '../ProfilePicture' import { capitalizeFirstLetter } from 'lib/utils' +import { useState } from 'react' + +import { ProfilePicture } from '../ProfilePicture' +import { LemonSelectMultiple, LemonSelectMultipleProps } from './LemonSelectMultiple' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.tsx b/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.tsx index dc324b2af77d6..9c085799bb855 100644 --- a/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.tsx +++ b/frontend/src/lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple.tsx @@ -1,8 +1,9 @@ +import './LemonSelectMultiple.scss' + import { Select } from 'antd' -import { range } from 'lib/utils' -import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import './LemonSelectMultiple.scss' +import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' +import { range } from 'lib/utils' import { ReactNode } from 'react' export interface LemonSelectMultipleOption { diff --git a/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.stories.tsx b/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.stories.tsx index 67850000a6417..26cd5a09660c8 100644 --- a/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.stories.tsx @@ -1,9 +1,9 @@ import { Meta } from '@storybook/react' - -import { LemonSkeleton } from './LemonSkeleton' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonSkeleton } from './LemonSkeleton' + const meta: Meta = { title: 'Lemon UI/Lemon Skeleton', component: LemonSkeleton, diff --git a/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.tsx b/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.tsx index 6524b6d573e7b..321e9c4071769 100644 --- a/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.tsx +++ b/frontend/src/lib/lemon-ui/LemonSkeleton/LemonSkeleton.tsx @@ -1,7 +1,8 @@ +import './LemonSkeleton.scss' + import clsx from 'clsx' -import { range } from 'lib/utils' import { LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import './LemonSkeleton.scss' +import { range } from 'lib/utils' export interface LemonSkeletonProps { className?: string diff --git a/frontend/src/lib/lemon-ui/LemonSkeleton/index.ts b/frontend/src/lib/lemon-ui/LemonSkeleton/index.ts index c29e265fe801a..206f9202dc085 100644 --- a/frontend/src/lib/lemon-ui/LemonSkeleton/index.ts +++ b/frontend/src/lib/lemon-ui/LemonSkeleton/index.ts @@ -1,2 +1,2 @@ -export { LemonSkeleton } from './LemonSkeleton' export type { LemonSkeletonProps } from './LemonSkeleton' +export { LemonSkeleton } from './LemonSkeleton' diff --git a/frontend/src/lib/lemon-ui/LemonSnack/LemonSnack.stories.tsx b/frontend/src/lib/lemon-ui/LemonSnack/LemonSnack.stories.tsx index 67bd0ed1a1787..e7e2c9528687d 100644 --- a/frontend/src/lib/lemon-ui/LemonSnack/LemonSnack.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSnack/LemonSnack.stories.tsx @@ -1,6 +1,7 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonSnack, LemonSnackProps } from './LemonSnack' + import { ProfilePicture } from '../ProfilePicture' +import { LemonSnack, LemonSnackProps } from './LemonSnack' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonSnack/index.ts b/frontend/src/lib/lemon-ui/LemonSnack/index.ts index 7e0c7cf81f3c3..b228c4eb995d1 100644 --- a/frontend/src/lib/lemon-ui/LemonSnack/index.ts +++ b/frontend/src/lib/lemon-ui/LemonSnack/index.ts @@ -1,2 +1,2 @@ -export { LemonSnack } from './LemonSnack' export type { LemonSnackProps } from './LemonSnack' +export { LemonSnack } from './LemonSnack' diff --git a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.scss b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.scss index 50b5e18e475d6..c785fbc3c53b6 100644 --- a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.scss +++ b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.scss @@ -1,4 +1,7 @@ .LemonSwitch { + --lemon-switch-height: 1.25rem; + --lemon-switch-width: 2.25rem; + width: fit-content; font-weight: 500; line-height: 1.5rem; @@ -46,6 +49,11 @@ cursor: not-allowed; // A label with for=* also toggles the switch, so it shouldn't have the text select cursor } } + + .posthog-3000 & { + --lemon-switch-height: 1.125rem; + --lemon-switch-width: calc(11 / 6 * var(--lemon-switch-height)); // Same proportion as in IconToggle + } } .LemonSwitch__button { @@ -53,8 +61,8 @@ display: inline-block; flex-shrink: 0; padding: 0; - width: 2.25rem; - height: 1.25rem; + width: var(--lemon-switch-width); + height: var(--lemon-switch-height); background: none; border: none; cursor: pointer; @@ -75,8 +83,21 @@ background-color: var(--border); transition: background-color 100ms ease; + .posthog-3000 & { + border-radius: var(--lemon-switch-height); + height: 100%; + width: 100%; + top: 0; + pointer-events: none; + background-color: var(--border-bold); + } + .LemonSwitch--checked & { background-color: var(--primary-highlight); + + .posthog-3000 & { + background-color: var(--primary-3000); + } } } @@ -89,23 +110,55 @@ border-radius: 0.625rem; background-color: #fff; border: 2px solid var(--border); - transition: background-color 100ms ease, transform 100ms ease, border-color 100ms ease; + transition: background-color 100ms ease, transform 100ms ease, width 100ms ease, border-color 100ms ease; cursor: inherit; display: flex; align-items: center; justify-content: center; + .posthog-3000 & { + --lemon-switch-handle-ratio: calc(3 / 4); // Same proportion as in IconToggle + --lemon-switch-handle-gutter: calc(var(--lemon-switch-height) * calc(1 - var(--lemon-switch-handle-ratio)) / 2); + --lemon-switch-handle-width: calc(var(--lemon-switch-height) * var(--lemon-switch-handle-ratio)); + --lemon-switch-active-translate: translateX( + calc(var(--lemon-switch-width) - var(--lemon-switch-handle-width) - var(--lemon-switch-handle-gutter) * 2) + ); + + top: var(--lemon-switch-handle-gutter); + left: var(--lemon-switch-handle-gutter); + width: var(--lemon-switch-handle-width); + height: calc(var(--lemon-switch-height) * var(--lemon-switch-handle-ratio)); + border: none; + pointer-events: none; + background-color: #fff; + } + .LemonSwitch--checked & { background-color: var(--primary); border-color: var(--primary); transform: translateX(1rem); + + .posthog-3000 & { + transform: var(--lemon-switch-active-translate); + background-color: #fff; + } } .LemonSwitch--active & { transform: scale(1.1); + + .posthog-3000 & { + --lemon-switch-handle-width: calc(var(--lemon-switch-height) * var(--lemon-switch-handle-ratio) * 1.2); + + transform: none; + } } .LemonSwitch--active.LemonSwitch--checked & { transform: translateX(1rem) scale(1.1); + + .posthog-3000 & { + transform: var(--lemon-switch-active-translate); + } } } diff --git a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.stories.tsx b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.stories.tsx index 8775d1f549972..29c7a81df6181 100644 --- a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.stories.tsx @@ -1,8 +1,8 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { IconGlobeLock } from 'lib/lemon-ui/icons' +import { useState } from 'react' import { LemonSwitch as RawLemonSwitch, LemonSwitchProps } from './LemonSwitch' -import { IconGlobeLock } from 'lib/lemon-ui/icons' const LemonSwitch = ({ checked, ...props }: Partial): JSX.Element => { const [isChecked, setIsChecked] = useState(checked || false) diff --git a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.tsx b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.tsx index 945a59523535b..b47ce51773ae1 100644 --- a/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.tsx +++ b/frontend/src/lib/lemon-ui/LemonSwitch/LemonSwitch.tsx @@ -1,7 +1,8 @@ -import clsx from 'clsx' -import { useMemo, useState } from 'react' import './LemonSwitch.scss' + +import clsx from 'clsx' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { useMemo, useState } from 'react' export interface LemonSwitchProps { className?: string @@ -79,7 +80,7 @@ export function LemonSwitch({ buttonComponent = ( {/* wrap it in a div so that the tooltip works even when disabled */} -
    {buttonComponent}
    +
    {buttonComponent}
    ) } diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.scss b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.scss index 6e37c6cd5eecc..4bffbac72b20e 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.scss +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.scss @@ -173,14 +173,14 @@ padding-bottom: 0.5rem; .posthog-3000 & { - padding-top: 0.3rem; - padding-bottom: 0.3rem; + padding-top: 0.3125rem; + padding-bottom: 0.3125rem; } .LemonButton { .posthog-3000 & { - margin-top: -0.2rem; - margin-bottom: -0.2rem; + margin-top: -0.25rem; + margin-bottom: -0.25rem; } } } diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.stories.tsx b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.stories.tsx index 95a7903db1374..751114946a32f 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.stories.tsx @@ -1,8 +1,9 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonTable, LemonTableProps } from './LemonTable' -import { LemonButton } from '../LemonButton' import { useEffect } from 'react' +import { LemonButton } from '../LemonButton' +import { LemonTable, LemonTableProps } from './LemonTable' + type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Lemon Table', diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.tsx b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.tsx index b3c5deae98491..d39b9ba9e3896 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTable.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTable.tsx @@ -1,18 +1,20 @@ +import './LemonTable.scss' + import clsx from 'clsx' import { useActions, useValues } from 'kea' import { router } from 'kea-router' +import { useScrollable } from 'lib/hooks/useScrollable' +import { IconInfo } from 'lib/lemon-ui/icons' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import React, { HTMLProps, useCallback, useEffect, useMemo, useState } from 'react' + +import { PaginationAuto, PaginationControl, PaginationManual, usePagination } from '../PaginationControl' import { Tooltip } from '../Tooltip' +import { LemonTableLoader } from './LemonTableLoader' +import { getNextSorting, Sorting, SortingIndicator } from './sorting' import { TableRow } from './TableRow' -import './LemonTable.scss' -import { Sorting, SortingIndicator, getNextSorting } from './sorting' import { ExpandableConfig, LemonTableColumn, LemonTableColumnGroup, LemonTableColumns } from './types' -import { PaginationAuto, PaginationControl, PaginationManual, usePagination } from '../PaginationControl' -import { useScrollable } from 'lib/hooks/useScrollable' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { LemonTableLoader } from './LemonTableLoader' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { IconInfo } from 'lib/lemon-ui/icons' /** * Determine the column's key, using `dataIndex` as fallback. diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss index 4e30b5ddec332..2f0d22e9fd1e8 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.scss @@ -22,7 +22,6 @@ background: var(--primary); .posthog-3000 & { - animation: loading-bar 1.5s linear infinite; background: var(--primary-3000); } } diff --git a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.tsx b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.tsx index 2b50878fb1e07..3b79a8651a47a 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/LemonTableLoader.tsx @@ -1,6 +1,7 @@ -import { CSSTransition } from 'react-transition-group' import './LemonTableLoader.scss' + import React from 'react' +import { CSSTransition } from 'react-transition-group' export function LemonTableLoader({ loading = false, diff --git a/frontend/src/lib/lemon-ui/LemonTable/TableCellSparkline.tsx b/frontend/src/lib/lemon-ui/LemonTable/TableCellSparkline.tsx index 226809d3c8b43..8a77bc49345f5 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/TableCellSparkline.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/TableCellSparkline.tsx @@ -1,10 +1,10 @@ -import { useEffect, useRef, useState } from 'react' -import { Chart, ChartItem, TooltipModel } from 'lib/Chart' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { offset } from '@floating-ui/react' - import './TableCellSparkline.scss' + +import { offset } from '@floating-ui/react' +import { Chart, ChartItem, TooltipModel } from 'lib/Chart' import { getColorVar } from 'lib/colors' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { useEffect, useRef, useState } from 'react' export function TableCellSparkline({ labels, data }: { labels?: string[]; data: number[] }): JSX.Element { const canvasRef = useRef(null) diff --git a/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx b/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx index 615c7c6afbb44..281a73ff3519a 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/TableRow.tsx @@ -1,8 +1,9 @@ -import React, { HTMLProps, useState } from 'react' +import clsx from 'clsx' import { IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import React, { HTMLProps, useState } from 'react' + import { ExpandableConfig, LemonTableColumnGroup, TableCellRepresentation } from './types' -import clsx from 'clsx' export interface TableRowProps> { record: T diff --git a/frontend/src/lib/lemon-ui/LemonTable/columnUtils.tsx b/frontend/src/lib/lemon-ui/LemonTable/columnUtils.tsx index 608c0b0b70657..d91e1ff31b3d7 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/columnUtils.tsx +++ b/frontend/src/lib/lemon-ui/LemonTable/columnUtils.tsx @@ -1,8 +1,10 @@ import { TZLabel } from 'lib/components/TZLabel' +import { Dayjs, dayjs } from 'lib/dayjs' + +import { UserBasicType } from '~/types' + import { ProfilePicture } from '../ProfilePicture' import { LemonTableColumn } from './types' -import { UserBasicType } from '~/types' -import { Dayjs, dayjs } from 'lib/dayjs' export function createdAtColumn(): LemonTableColumn { return { diff --git a/frontend/src/lib/lemon-ui/LemonTable/index.ts b/frontend/src/lib/lemon-ui/LemonTable/index.ts index ce12c3ad40fd3..f375acd2dffa5 100644 --- a/frontend/src/lib/lemon-ui/LemonTable/index.ts +++ b/frontend/src/lib/lemon-ui/LemonTable/index.ts @@ -1,4 +1,4 @@ -export { LemonTable } from './LemonTable' export type { LemonTableProps } from './LemonTable' +export { LemonTable } from './LemonTable' export type { Sorting } from './sorting' export type { ExpandableConfig, LemonTableColumn, LemonTableColumnGroup, LemonTableColumns } from './types' diff --git a/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.stories.tsx b/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.stories.tsx index 9d1e6532cecfc..9618ab6215934 100644 --- a/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useState } from 'react' + import { LemonTab, LemonTabs as LemonTabsComponent } from './LemonTabs' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.tsx b/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.tsx index 873c47327ddbb..d94f9732623cd 100644 --- a/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.tsx +++ b/frontend/src/lib/lemon-ui/LemonTabs/LemonTabs.tsx @@ -1,10 +1,12 @@ +import './LemonTabs.scss' + import clsx from 'clsx' import { AlignType } from 'rc-trigger/lib/interface' + import { useSliderPositioning } from '../hooks' import { IconInfo } from '../icons' -import { Tooltip } from '../Tooltip' -import './LemonTabs.scss' import { Link } from '../Link' +import { Tooltip } from '../Tooltip' /** A tab that represents one of the options, but doesn't have any content. Render tab-dependent UI yourself. */ export interface AbstractLemonTab { diff --git a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.stories.tsx b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.stories.tsx index 1c5403b05f0c8..bf68c9db7d5a0 100644 --- a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { LemonTag as LemonTagComponent, LemonTagType } from './LemonTag' type Story = StoryObj diff --git a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.tsx b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.tsx index 3d7c36494ab4a..564509e905c65 100644 --- a/frontend/src/lib/lemon-ui/LemonTag/LemonTag.tsx +++ b/frontend/src/lib/lemon-ui/LemonTag/LemonTag.tsx @@ -1,8 +1,9 @@ +import './LemonTag.scss' + import clsx from 'clsx' import { IconClose, IconEllipsis } from 'lib/lemon-ui/icons' import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' import { LemonButtonDropdown } from 'lib/lemon-ui/LemonButton/LemonButton' -import './LemonTag.scss' export type LemonTagType = | 'primary' diff --git a/frontend/src/lib/lemon-ui/LemonTag/index.ts b/frontend/src/lib/lemon-ui/LemonTag/index.ts index 217aec8ebd633..540d69cfadf7b 100644 --- a/frontend/src/lib/lemon-ui/LemonTag/index.ts +++ b/frontend/src/lib/lemon-ui/LemonTag/index.ts @@ -1,2 +1,2 @@ -export { LemonTag } from './LemonTag' export type { LemonTagProps, LemonTagType } from './LemonTag' +export { LemonTag } from './LemonTag' diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx index ea0f7ee62571a..c0e55962e477c 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.stories.tsx @@ -1,7 +1,7 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { useState } from 'react' -import { LemonTextArea, LemonTextAreaProps, LemonTextAreaMarkdown as _LemonTextMarkdown } from './LemonTextArea' +import { LemonTextArea, LemonTextAreaMarkdown as _LemonTextMarkdown, LemonTextAreaProps } from './LemonTextArea' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx index 77c94c64aad0f..dbc2181d76b05 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx +++ b/frontend/src/lib/lemon-ui/LemonTextArea/LemonTextArea.tsx @@ -1,18 +1,20 @@ import './LemonTextArea.scss' -import React, { createRef, useRef, useState } from 'react' + import clsx from 'clsx' -import TextareaAutosize from 'react-textarea-autosize' -import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons' +import { useValues } from 'kea' import { TextContent } from 'lib/components/Cards/TextCard/TextCard' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import posthog from 'posthog-js' +import { useUploadFiles } from 'lib/hooks/useUploadFiles' +import { IconMarkdown, IconTools } from 'lib/lemon-ui/icons' import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput' -import { useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { Link } from 'lib/lemon-ui/Link' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import posthog from 'posthog-js' +import React, { createRef, useRef, useState } from 'react' +import TextareaAutosize from 'react-textarea-autosize' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { LemonTabs } from '../LemonTabs' -import { useUploadFiles } from 'lib/hooks/useUploadFiles' export interface LemonTextAreaProps extends Pick< diff --git a/frontend/src/lib/lemon-ui/LemonTextArea/index.ts b/frontend/src/lib/lemon-ui/LemonTextArea/index.ts index 9276620a4db25..138d5df39dcf0 100644 --- a/frontend/src/lib/lemon-ui/LemonTextArea/index.ts +++ b/frontend/src/lib/lemon-ui/LemonTextArea/index.ts @@ -1,2 +1,2 @@ -export { LemonTextArea, LemonTextAreaMarkdown } from './LemonTextArea' export type { LemonTextAreaProps } from './LemonTextArea' +export { LemonTextArea, LemonTextAreaMarkdown } from './LemonTextArea' diff --git a/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.tsx b/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.tsx index 232a68b23fd12..9747b5c249a11 100644 --- a/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.tsx +++ b/frontend/src/lib/lemon-ui/LemonWidget/LemonWidget.tsx @@ -1,8 +1,10 @@ -import { LemonButton } from '../LemonButton' -import { IconClose } from '../icons' import './LemonWidget.scss' + import clsx from 'clsx' +import { IconClose } from '../icons' +import { LemonButton } from '../LemonButton' + export interface LemonWidgetProps { title: string onClose?: () => void diff --git a/frontend/src/lib/lemon-ui/LemonWidget/index.ts b/frontend/src/lib/lemon-ui/LemonWidget/index.ts index 3577ddd8cb037..eee1745c27a29 100644 --- a/frontend/src/lib/lemon-ui/LemonWidget/index.ts +++ b/frontend/src/lib/lemon-ui/LemonWidget/index.ts @@ -1,2 +1,2 @@ -export { LemonWidget } from './LemonWidget' export type { LemonWidgetProps } from './LemonWidget' +export { LemonWidget } from './LemonWidget' diff --git a/frontend/src/lib/lemon-ui/Lettermark/Lettermark.stories.tsx b/frontend/src/lib/lemon-ui/Lettermark/Lettermark.stories.tsx index 2a1eb5aa8a757..292fc93b2c71b 100644 --- a/frontend/src/lib/lemon-ui/Lettermark/Lettermark.stories.tsx +++ b/frontend/src/lib/lemon-ui/Lettermark/Lettermark.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { Lettermark, LettermarkColor, LettermarkProps } from './Lettermark' import { range } from 'lib/utils' +import { Lettermark, LettermarkColor, LettermarkProps } from './Lettermark' + type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Lettermark', diff --git a/frontend/src/lib/lemon-ui/Lettermark/Lettermark.tsx b/frontend/src/lib/lemon-ui/Lettermark/Lettermark.tsx index a60be3adaa15f..e0d88847ec6f6 100644 --- a/frontend/src/lib/lemon-ui/Lettermark/Lettermark.tsx +++ b/frontend/src/lib/lemon-ui/Lettermark/Lettermark.tsx @@ -1,6 +1,7 @@ -import clsx from 'clsx' import './Lettermark.scss' +import clsx from 'clsx' + // This is the number of known --lettermark-* variables in `globals.scss` const NUM_LETTERMARK_STYLES = 8 diff --git a/frontend/src/lib/lemon-ui/Lettermark/index.ts b/frontend/src/lib/lemon-ui/Lettermark/index.ts index 3f8c05eaa269e..ef89108074f7e 100644 --- a/frontend/src/lib/lemon-ui/Lettermark/index.ts +++ b/frontend/src/lib/lemon-ui/Lettermark/index.ts @@ -1,2 +1,2 @@ -export { Lettermark, LettermarkColor } from './Lettermark' export type { LettermarkProps } from './Lettermark' +export { Lettermark, LettermarkColor } from './Lettermark' diff --git a/frontend/src/lib/lemon-ui/Link/Link.stories.tsx b/frontend/src/lib/lemon-ui/Link/Link.stories.tsx index 38142d11ac702..638030e19d0e7 100644 --- a/frontend/src/lib/lemon-ui/Link/Link.stories.tsx +++ b/frontend/src/lib/lemon-ui/Link/Link.stories.tsx @@ -1,7 +1,8 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { Link, LinkProps } from './Link' import { urls } from 'scenes/urls' +import { Link, LinkProps } from './Link' + type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Link', diff --git a/frontend/src/lib/lemon-ui/Link/Link.tsx b/frontend/src/lib/lemon-ui/Link/Link.tsx index 5365eb5213073..4677818b2b529 100644 --- a/frontend/src/lib/lemon-ui/Link/Link.tsx +++ b/frontend/src/lib/lemon-ui/Link/Link.tsx @@ -1,16 +1,18 @@ -import React from 'react' -import { router } from 'kea-router' -import { isExternalLink } from 'lib/utils' -import clsx from 'clsx' import './Link.scss' -import { IconOpenInNew } from '../icons' -import { Tooltip } from '../Tooltip' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' -import { useActions } from 'kea' +import clsx from 'clsx' +import { useActions } from 'kea' +import { router } from 'kea-router' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { isExternalLink } from 'lib/utils' +import React from 'react' import { useNotebookDrag } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' + import { sidePanelDocsLogic } from '~/layout/navigation-3000/sidepanel/panels/sidePanelDocsLogic' +import { IconOpenInNew } from '../icons' +import { Tooltip } from '../Tooltip' + type RoutePart = string | Record export type LinkProps = Pick, 'target' | 'className' | 'children' | 'title'> & { diff --git a/frontend/src/lib/lemon-ui/Link/index.ts b/frontend/src/lib/lemon-ui/Link/index.ts index ca0be19dee84a..0442a73e1dbca 100644 --- a/frontend/src/lib/lemon-ui/Link/index.ts +++ b/frontend/src/lib/lemon-ui/Link/index.ts @@ -1,2 +1,2 @@ -export { Link } from './Link' export type { LinkProps } from './Link' +export { Link } from './Link' diff --git a/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.stories.tsx b/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.stories.tsx index b2522a0b08c7a..8953d363d7daf 100644 --- a/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.stories.tsx +++ b/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' + import { PaginationControl, PaginationControlProps } from './PaginationControl' import { usePagination } from './usePagination' diff --git a/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.tsx b/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.tsx index b26cfdb485f5c..578c0c5f648b7 100644 --- a/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.tsx +++ b/frontend/src/lib/lemon-ui/PaginationControl/PaginationControl.tsx @@ -1,8 +1,10 @@ +import './PaginationControl.scss' + +import clsx from 'clsx' import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import './PaginationControl.scss' + import { PaginationState } from './types' -import clsx from 'clsx' export interface PaginationControlProps extends PaginationState { nouns?: [string, string] diff --git a/frontend/src/lib/lemon-ui/PaginationControl/index.ts b/frontend/src/lib/lemon-ui/PaginationControl/index.ts index 4328e3614fc19..7ec775564dc7e 100644 --- a/frontend/src/lib/lemon-ui/PaginationControl/index.ts +++ b/frontend/src/lib/lemon-ui/PaginationControl/index.ts @@ -1,4 +1,4 @@ -export { PaginationControl } from './PaginationControl' -export { usePagination } from './usePagination' export type { PaginationControlProps } from './PaginationControl' +export { PaginationControl } from './PaginationControl' export type { PaginationAuto, PaginationManual, PaginationState } from './types' +export { usePagination } from './usePagination' diff --git a/frontend/src/lib/lemon-ui/PaginationControl/usePagination.ts b/frontend/src/lib/lemon-ui/PaginationControl/usePagination.ts index 24beda803877b..8b8e74350ecd4 100644 --- a/frontend/src/lib/lemon-ui/PaginationControl/usePagination.ts +++ b/frontend/src/lib/lemon-ui/PaginationControl/usePagination.ts @@ -1,6 +1,7 @@ import { useActions, useValues } from 'kea' import { router } from 'kea-router' import { useCallback, useMemo } from 'react' + import { PaginationAuto, PaginationManual, PaginationState } from './types' export function usePagination( diff --git a/frontend/src/lib/lemon-ui/Popover/Popover.stories.tsx b/frontend/src/lib/lemon-ui/Popover/Popover.stories.tsx index 477f44fd52d9f..dba48ee486eb8 100644 --- a/frontend/src/lib/lemon-ui/Popover/Popover.stories.tsx +++ b/frontend/src/lib/lemon-ui/Popover/Popover.stories.tsx @@ -1,18 +1,13 @@ -import { StoryFn, Meta, StoryObj } from '@storybook/react' +import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { IconArrowDropDown } from 'lib/lemon-ui/icons' import { Popover } from './Popover' -import { IconArrowDropDown } from 'lib/lemon-ui/icons' type Story = StoryObj const meta: Meta = { title: 'Lemon UI/Popover', component: Popover, - parameters: { - testOptions: { - skip: true, // FIXME: This story needs a play test for the popup to show up in snapshots - }, - }, - tags: ['autodocs'], + tags: ['autodocs', 'test-skip'], // FIXME: This story needs a play test for the popup to show up in snapshots } export default meta diff --git a/frontend/src/lib/lemon-ui/Popover/Popover.tsx b/frontend/src/lib/lemon-ui/Popover/Popover.tsx index 2081fac8edb85..f77c7c6e49a42 100644 --- a/frontend/src/lib/lemon-ui/Popover/Popover.tsx +++ b/frontend/src/lib/lemon-ui/Popover/Popover.tsx @@ -1,22 +1,23 @@ import './Popover.scss' -import React, { MouseEventHandler, ReactElement, useContext, useEffect, useLayoutEffect, useRef } from 'react' -import { CLICK_OUTSIDE_BLOCK_CLASS, useOutsideClickHandler } from 'lib/hooks/useOutsideClickHandler' -import clsx from 'clsx' + import { - useFloating, + arrow, autoUpdate, + flip, + FloatingPortal, Middleware, Placement, shift, - flip, size, - arrow, - FloatingPortal, + useFloating, UseFloatingReturn, useMergeRefs, } from '@floating-ui/react' -import { CSSTransition } from 'react-transition-group' +import clsx from 'clsx' import { useEventListener } from 'lib/hooks/useEventListener' +import { CLICK_OUTSIDE_BLOCK_CLASS, useOutsideClickHandler } from 'lib/hooks/useOutsideClickHandler' +import React, { MouseEventHandler, ReactElement, useContext, useEffect, useLayoutEffect, useRef } from 'react' +import { CSSTransition } from 'react-transition-group' export interface PopoverProps { ref?: React.MutableRefObject | React.Ref | null diff --git a/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.stories.tsx b/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.stories.tsx index 821b2abfc42e5..96c6d6fa7f1f4 100644 --- a/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.stories.tsx +++ b/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.stories.tsx @@ -1,7 +1,8 @@ -import { ProfileBubbles as ProfileBubblesComponent, ProfileBubblesProps } from './ProfileBubbles' import { Meta } from '@storybook/react' import { alphabet, range } from 'lib/utils' +import { ProfileBubbles as ProfileBubblesComponent, ProfileBubblesProps } from './ProfileBubbles' + const DUMMIES: ProfileBubblesProps['people'] = [ { email: 'michael@posthog.com', name: 'Michael' }, { email: 'lottie@posthog.com', name: 'Lottie' }, diff --git a/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.tsx b/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.tsx index f9cbd5ff25255..23cd6b167efeb 100644 --- a/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.tsx +++ b/frontend/src/lib/lemon-ui/ProfilePicture/ProfileBubbles.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx' -import { ProfilePicture } from '.' + import { Tooltip } from '../Tooltip' +import { ProfilePicture } from '.' export interface ProfileBubblesProps extends React.HTMLProps { people: { email: string; name?: string; title?: string }[] diff --git a/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.tsx b/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.tsx index 80f308b960191..07fb82adffecd 100644 --- a/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.tsx +++ b/frontend/src/lib/lemon-ui/ProfilePicture/ProfilePicture.tsx @@ -1,12 +1,14 @@ +import './ProfilePicture.scss' + import clsx from 'clsx' import { useValues } from 'kea' +import { inStorybookTestRunner } from 'lib/utils' import md5 from 'md5' import { useEffect, useState } from 'react' import { userLogic } from 'scenes/userLogic' + import { IconRobot } from '../icons' import { Lettermark, LettermarkColor } from '../Lettermark/Lettermark' -import './ProfilePicture.scss' -import { inStorybookTestRunner } from 'lib/utils' export interface ProfilePictureProps { name?: string diff --git a/frontend/src/lib/lemon-ui/ProfilePicture/index.ts b/frontend/src/lib/lemon-ui/ProfilePicture/index.ts index 00937f134c370..2cd1f985d3e99 100644 --- a/frontend/src/lib/lemon-ui/ProfilePicture/index.ts +++ b/frontend/src/lib/lemon-ui/ProfilePicture/index.ts @@ -1,4 +1,4 @@ -export { ProfilePicture } from './ProfilePicture' -export type { ProfilePictureProps } from './ProfilePicture' -export { ProfileBubbles } from './ProfileBubbles' export type { ProfileBubblesProps } from './ProfileBubbles' +export { ProfileBubbles } from './ProfileBubbles' +export type { ProfilePictureProps } from './ProfilePicture' +export { ProfilePicture } from './ProfilePicture' diff --git a/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx b/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx index 1ebec0c0a5213..e1625461109a8 100644 --- a/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx +++ b/frontend/src/lib/lemon-ui/Spinner/Spinner.stories.tsx @@ -1,7 +1,7 @@ +import { LemonButton } from '@posthog/lemon-ui' import { Meta } from '@storybook/react' import { Spinner as Spinner, SpinnerOverlay } from './Spinner' -import { LemonButton } from '@posthog/lemon-ui' const meta: Meta = { title: 'Lemon UI/Spinner', diff --git a/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx b/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx index 3c613721602bb..5939bc14ec114 100644 --- a/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx +++ b/frontend/src/lib/lemon-ui/Spinner/Spinner.tsx @@ -1,6 +1,7 @@ -import clsx from 'clsx' import './Spinner.scss' +import clsx from 'clsx' + export interface SpinnerProps { textColored?: boolean className?: string diff --git a/frontend/src/lib/lemon-ui/Splotch/Splotch.stories.tsx b/frontend/src/lib/lemon-ui/Splotch/Splotch.stories.tsx index 821f6d29141a0..a61e437ed1ae3 100644 --- a/frontend/src/lib/lemon-ui/Splotch/Splotch.stories.tsx +++ b/frontend/src/lib/lemon-ui/Splotch/Splotch.stories.tsx @@ -1,4 +1,5 @@ import { Meta, StoryFn } from '@storybook/react' + import { Splotch, SplotchColor, SplotchProps } from './Splotch' const meta: Meta = { diff --git a/frontend/src/lib/lemon-ui/Splotch/Splotch.tsx b/frontend/src/lib/lemon-ui/Splotch/Splotch.tsx index e8a91a09f7b21..e3f9a2bbac587 100644 --- a/frontend/src/lib/lemon-ui/Splotch/Splotch.tsx +++ b/frontend/src/lib/lemon-ui/Splotch/Splotch.tsx @@ -1,6 +1,7 @@ -import clsx from 'clsx' import './Splotch.scss' +import clsx from 'clsx' + export enum SplotchColor { Purple = 'purple', Blue = 'blue', diff --git a/frontend/src/lib/lemon-ui/Tooltip/Tooltip.tsx b/frontend/src/lib/lemon-ui/Tooltip/Tooltip.tsx index 04ebbd7e00412..87b6182f61d81 100644 --- a/frontend/src/lib/lemon-ui/Tooltip/Tooltip.tsx +++ b/frontend/src/lib/lemon-ui/Tooltip/Tooltip.tsx @@ -1,7 +1,6 @@ -import React, { useState } from 'react' -// eslint-disable-next-line no-restricted-imports import { Tooltip as AntdTooltip } from 'antd' import { TooltipProps as AntdTooltipProps } from 'antd/lib/tooltip' +import React, { useState } from 'react' import { useDebounce } from 'use-debounce' const DEFAULT_DELAY_MS = 500 diff --git a/frontend/src/lib/lemon-ui/Tooltip/index.ts b/frontend/src/lib/lemon-ui/Tooltip/index.ts index eaca424fb1663..31867601a614d 100644 --- a/frontend/src/lib/lemon-ui/Tooltip/index.ts +++ b/frontend/src/lib/lemon-ui/Tooltip/index.ts @@ -1,2 +1,2 @@ -export { Tooltip } from './Tooltip' export type { TooltipProps } from './Tooltip' +export { Tooltip } from './Tooltip' diff --git a/frontend/src/lib/lemon-ui/colors.stories.tsx b/frontend/src/lib/lemon-ui/colors.stories.tsx index d8d9573ac68b0..304661610cb2e 100644 --- a/frontend/src/lib/lemon-ui/colors.stories.tsx +++ b/frontend/src/lib/lemon-ui/colors.stories.tsx @@ -1,7 +1,8 @@ import { Meta } from '@storybook/react' -import { Popover } from './Popover/Popover' import { useState } from 'react' + import { LemonTable } from './LemonTable' +import { Popover } from './Popover/Popover' const meta: Meta = { title: 'Lemon UI/Colors', diff --git a/frontend/src/lib/lemon-ui/icons/icons.stories.tsx b/frontend/src/lib/lemon-ui/icons/icons.stories.tsx index cb09face4657f..2c7c61429d41f 100644 --- a/frontend/src/lib/lemon-ui/icons/icons.stories.tsx +++ b/frontend/src/lib/lemon-ui/icons/icons.stories.tsx @@ -1,9 +1,10 @@ -import * as React from 'react' -import * as icons from './icons' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { LemonTable } from 'lib/lemon-ui/LemonTable' -import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' +import { LemonTable } from 'lib/lemon-ui/LemonTable' +import * as React from 'react' + +import * as icons from './icons' const { IconGauge, IconWithCount } = icons @@ -104,7 +105,7 @@ const LibraryTemplate: StoryFn<{ letter?: string | null }> = ({ letter }) => { // This is for actual Storybook users export const Library: LibraryType = LibraryTemplate.bind({}) -Library.parameters = { testOptions: { skip: true } } +Library.tags = ['autodocs', 'test-skip'] // These are just for snapshots. As opposed to the full library, the stories below are segmented by the first letter // of the icon name, which greatly optimizes both the UX and storage aspects of diffing snapshots. diff --git a/frontend/src/lib/lemon-ui/icons/icons.tsx b/frontend/src/lib/lemon-ui/icons/icons.tsx index 429bd17c3c6eb..aa04067a78ba7 100644 --- a/frontend/src/lib/lemon-ui/icons/icons.tsx +++ b/frontend/src/lib/lemon-ui/icons/icons.tsx @@ -1,8 +1,9 @@ // Loads custom icons (some icons may come from a third-party library) -import clsx from 'clsx' -import { CSSProperties, PropsWithChildren, SVGAttributes } from 'react' import './icons.scss' + +import clsx from 'clsx' import { LemonBadge, LemonBadgeProps } from 'lib/lemon-ui/LemonBadge' +import { CSSProperties, PropsWithChildren, SVGAttributes } from 'react' interface IconWithCountProps { count: number diff --git a/frontend/src/lib/lemon-ui/lemonToast.tsx b/frontend/src/lib/lemon-ui/lemonToast.tsx index 5332a95b0599a..5af288acc6c96 100644 --- a/frontend/src/lib/lemon-ui/lemonToast.tsx +++ b/frontend/src/lib/lemon-ui/lemonToast.tsx @@ -1,8 +1,9 @@ -import { toast, ToastContentProps as ToastifyRenderProps, ToastOptions } from 'react-toastify' import { IconCheckmark, IconClose, IconErrorOutline, IconInfo, IconWarning } from 'lib/lemon-ui/icons' -import { LemonButton } from './LemonButton' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import posthog from 'posthog-js' +import { toast, ToastContentProps as ToastifyRenderProps, ToastOptions } from 'react-toastify' + +import { LemonButton } from './LemonButton' export function ToastCloseButton({ closeToast }: { closeToast?: () => void }): JSX.Element { return ( diff --git a/frontend/src/lib/logic/featureFlagLogic.ts b/frontend/src/lib/logic/featureFlagLogic.ts index 061bca49a70ea..592ea0f6646d7 100644 --- a/frontend/src/lib/logic/featureFlagLogic.ts +++ b/frontend/src/lib/logic/featureFlagLogic.ts @@ -1,9 +1,11 @@ -import { kea, path, actions, reducers, afterMount } from 'kea' -import type { featureFlagLogicType } from './featureFlagLogicType' -import posthog from 'posthog-js' +import { actions, afterMount, kea, path, reducers } from 'kea' import { getAppContext } from 'lib/utils/getAppContext' +import posthog from 'posthog-js' + import { AppContext } from '~/types' +import type { featureFlagLogicType } from './featureFlagLogicType' + export type FeatureFlagsSet = { [flag: string]: boolean | string } diff --git a/frontend/src/lib/logic/inAppPrompt/inAppPromptEventCaptureLogic.ts b/frontend/src/lib/logic/inAppPrompt/inAppPromptEventCaptureLogic.ts index 4eaf9dbabb158..b8ede572b2214 100644 --- a/frontend/src/lib/logic/inAppPrompt/inAppPromptEventCaptureLogic.ts +++ b/frontend/src/lib/logic/inAppPrompt/inAppPromptEventCaptureLogic.ts @@ -1,6 +1,7 @@ -import { kea, path, actions, listeners } from 'kea' -import type { inAppPromptEventCaptureLogicType } from './inAppPromptEventCaptureLogicType' +import { actions, kea, listeners, path } from 'kea' import posthog from 'posthog-js' + +import type { inAppPromptEventCaptureLogicType } from './inAppPromptEventCaptureLogicType' import { PromptType } from './inAppPromptLogic' const inAppPromptEventCaptureLogic = kea([ diff --git a/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.test.ts b/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.test.ts index 16d849e169079..e1a029a9d2db0 100644 --- a/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.test.ts +++ b/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.test.ts @@ -1,12 +1,14 @@ +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { inAppPromptLogic, PromptConfig, PromptUserState } from './inAppPromptLogic' +import api from 'lib/api' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { router } from 'kea-router' import { urls } from 'scenes/urls' + import { useMocks } from '~/mocks/jest' -import api from 'lib/api' +import { initKeaTests } from '~/test/init' + import { inAppPromptEventCaptureLogic } from './inAppPromptEventCaptureLogic' +import { inAppPromptLogic, PromptConfig, PromptUserState } from './inAppPromptLogic' const configProductTours: PromptConfig & { state: PromptUserState } = { sequences: [ diff --git a/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.tsx b/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.tsx index 3f725a72d4025..4c5cd22f04084 100644 --- a/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.tsx +++ b/frontend/src/lib/logic/inAppPrompt/inAppPromptLogic.tsx @@ -1,20 +1,12 @@ -import { createRoot } from 'react-dom/client' import { Placement } from '@floating-ui/react' -import { kea, path, actions, reducers, listeners, selectors, connect, afterMount, beforeUnmount } from 'kea' -import type { inAppPromptLogicType } from './inAppPromptLogicType' +import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { router, urlToAction } from 'kea-router' -import { - LemonActionableTooltip, - LemonActionableTooltipProps, -} from 'lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip' -import { inAppPromptEventCaptureLogic } from './inAppPromptEventCaptureLogic' import api from 'lib/api' import { now } from 'lib/dayjs' -import wcmatch from 'wildcard-match' import { - IconUnverifiedEvent, IconApps, IconBarChart, + IconCoffee, IconCohort, IconComment, IconExperiment, @@ -25,10 +17,19 @@ import { IconPerson, IconRecording, IconTools, - IconCoffee, IconTrendUp, + IconUnverifiedEvent, } from 'lib/lemon-ui/icons' +import { + LemonActionableTooltip, + LemonActionableTooltipProps, +} from 'lib/lemon-ui/LemonActionableTooltip/LemonActionableTooltip' import { Lettermark } from 'lib/lemon-ui/Lettermark' +import { createRoot } from 'react-dom/client' +import wcmatch from 'wildcard-match' + +import { inAppPromptEventCaptureLogic } from './inAppPromptEventCaptureLogic' +import type { inAppPromptLogicType } from './inAppPromptLogicType' /** To be extended with other types of notifications e.g. modals, bars */ export type PromptType = 'tooltip' diff --git a/frontend/src/lib/logic/newPrompt/Prompt.tsx b/frontend/src/lib/logic/newPrompt/Prompt.tsx index 37c2a473a6777..8392dfc95aa35 100644 --- a/frontend/src/lib/logic/newPrompt/Prompt.tsx +++ b/frontend/src/lib/logic/newPrompt/Prompt.tsx @@ -1,11 +1,14 @@ -import { useActions, useValues } from 'kea' import './prompt.scss' -import { promptLogic } from './promptLogic' -import clsx from 'clsx' + import { LemonButton, LemonModal } from '@posthog/lemon-ui' -import { PromptButtonType, PromptFlag, PromptPayload } from '~/types' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { FallbackCoverImage } from 'lib/components/FallbackCoverImage/FallbackCoverImage' +import { PromptButtonType, PromptFlag, PromptPayload } from '~/types' + +import { promptLogic } from './promptLogic' + export function ModalPrompt({ payload, closePrompt, diff --git a/frontend/src/lib/logic/newPrompt/prompt.stories.tsx b/frontend/src/lib/logic/newPrompt/prompt.stories.tsx index 59f9416017cf9..58eb6c9647db9 100644 --- a/frontend/src/lib/logic/newPrompt/prompt.stories.tsx +++ b/frontend/src/lib/logic/newPrompt/prompt.stories.tsx @@ -1,9 +1,11 @@ import { Meta } from '@storybook/react' import { useActions } from 'kea' +import BlankDashboardHog from 'public/blank-dashboard-hog.png' + import { PromptFlag, PromptPayload } from '~/types' + import { ModalPrompt, PopupPrompt, Prompt } from './Prompt' import { promptLogic } from './promptLogic' -import BlankDashboardHog from 'public/blank-dashboard-hog.png' const meta: Meta = { title: 'Components/Prompts', diff --git a/frontend/src/lib/logic/newPrompt/promptLogic.tsx b/frontend/src/lib/logic/newPrompt/promptLogic.tsx index ec397f791f321..064fcff1c78cd 100644 --- a/frontend/src/lib/logic/newPrompt/promptLogic.tsx +++ b/frontend/src/lib/logic/newPrompt/promptLogic.tsx @@ -1,10 +1,10 @@ import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' - -import posthog from 'posthog-js' -import { featureFlagLogic } from '../featureFlagLogic' import { router } from 'kea-router' +import posthog from 'posthog-js' + import { PromptButtonType, PromptFlag, PromptPayload } from '~/types' +import { featureFlagLogic } from '../featureFlagLogic' import type { promptLogicType } from './promptLogicType' const PROMPT_PREFIX = 'prompt' diff --git a/frontend/src/lib/logic/promptLogic.tsx b/frontend/src/lib/logic/promptLogic.tsx index 85a86f234c856..473bab99a655f 100644 --- a/frontend/src/lib/logic/promptLogic.tsx +++ b/frontend/src/lib/logic/promptLogic.tsx @@ -1,6 +1,6 @@ +import { Form, FormItemProps, Input, InputProps, Modal, ModalProps } from 'antd' +import { actions, events, kea, key, listeners, path, props } from 'kea' import { createRoot } from 'react-dom/client' -import { kea, props, path, key, actions, events, listeners } from 'kea' -import { Modal, ModalProps, Input, InputProps, Form, FormItemProps } from 'antd' import type { promptLogicType } from './promptLogicType' diff --git a/frontend/src/lib/taxonomy.tsx b/frontend/src/lib/taxonomy.tsx index a8f5774ab5b24..10be42e8fd679 100644 --- a/frontend/src/lib/taxonomy.tsx +++ b/frontend/src/lib/taxonomy.tsx @@ -1,4 +1,5 @@ import { KeyMapping, KeyMappingInterface, PropertyFilterValue } from '~/types' + import { Link } from './lemon-ui/Link' // If adding event properties with labels, check whether they should be added to diff --git a/frontend/src/lib/utils.test.ts b/frontend/src/lib/utils.test.ts index b9a49d899dc69..dca123df90749 100644 --- a/frontend/src/lib/utils.test.ts +++ b/frontend/src/lib/utils.test.ts @@ -1,4 +1,8 @@ +import { dayjs } from 'lib/dayjs' import tk from 'timekeeper' + +import { ElementType, EventType, PropertyType, TimeUnitType } from '~/types' + import { areObjectValuesEmpty, average, @@ -22,27 +26,25 @@ import { getFormattedLastWeekDate, hexToRGBA, humanFriendlyDuration, + humanFriendlyLargeNumber, identifierToHuman, isExternalLink, isURL, median, midEllipsis, numericOperatorMap, - objectDiffShallow, objectClean, objectCleanWithEmpty, + objectDiffShallow, pluralize, range, reverseColonDelimitedDuration, roundToDecimal, selectorOperatorMap, + shortTimeZone, stringOperatorMap, toParams, - shortTimeZone, - humanFriendlyLargeNumber, } from './utils' -import { ElementType, EventType, PropertyType, TimeUnitType } from '~/types' -import { dayjs } from 'lib/dayjs' describe('toParams', () => { it('handles unusual input', () => { diff --git a/frontend/src/lib/utils.tsx b/frontend/src/lib/utils.tsx index dd91147876a95..b37a126d607e8 100644 --- a/frontend/src/lib/utils.tsx +++ b/frontend/src/lib/utils.tsx @@ -1,4 +1,11 @@ +import * as Sentry from '@sentry/react' +import equal from 'fast-deep-equal' +import { tagColors } from 'lib/colors' +import { WEBHOOK_SERVICES } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { AlignType } from 'rc-trigger/lib/interface' import { CSSProperties } from 'react' + import { ActionType, ActorType, @@ -9,14 +16,9 @@ import { PropertyType, TimeUnitType, } from '~/types' -import * as Sentry from '@sentry/react' -import equal from 'fast-deep-equal' -import { tagColors } from 'lib/colors' -import { WEBHOOK_SERVICES } from 'lib/constants' -import { AlignType } from 'rc-trigger/lib/interface' -import { dayjs } from 'lib/dayjs' -import { getAppContext } from './utils/getAppContext' + import { CUSTOM_OPTION_KEY } from './components/DateFilter/types' +import { getAppContext } from './utils/getAppContext' /** * WARNING: Be very careful importing things here. This file is heavily used and can trigger a lot of cyclic imports diff --git a/frontend/src/lib/utils/d3Utils.ts b/frontend/src/lib/utils/d3Utils.ts index 8accdaaebf132..4c2814e6120bc 100644 --- a/frontend/src/lib/utils/d3Utils.ts +++ b/frontend/src/lib/utils/d3Utils.ts @@ -1,6 +1,6 @@ import * as d3 from 'd3' -import { INITIAL_CONFIG } from 'scenes/insights/views/Histogram/histogramUtils' import { D3Selector, D3Transition } from 'lib/hooks/useD3' +import { INITIAL_CONFIG } from 'scenes/insights/views/Histogram/histogramUtils' export const getOrCreateEl = ( container: D3Selector, diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index a94f5989178e8..e0a34b568417d 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -1,49 +1,51 @@ -import { kea, path, connect, actions, listeners } from 'kea' +import { actions, connect, kea, listeners, path } from 'kea' +import { convertPropertyGroupToProperties, isGroupPropertyFilter } from 'lib/components/PropertyFilters/utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import type { Dayjs } from 'lib/dayjs' +import { now } from 'lib/dayjs' import { isPostHogProp, keyMappingKeys } from 'lib/taxonomy' import posthog from 'posthog-js' +import { + isFilterWithDisplay, + isFunnelsFilter, + isPathsFilter, + isRetentionFilter, + isStickinessFilter, + isTrendsFilter, +} from 'scenes/insights/sharedUtils' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { EventIndex } from 'scenes/session-recordings/player/eventIndex' +import { SurveyTemplateType } from 'scenes/surveys/constants' import { userLogic } from 'scenes/userLogic' -import type { eventUsageLogicType } from './eventUsageLogicType' + import { - FilterType, - DashboardType, - PersonType, + AccessLevel, + AnyPartialFilterType, + AnyPropertyFilter, DashboardMode, + DashboardType, EntityType, + Experiment, + FilterLogicalOperator, + FilterType, + FunnelCorrelation, + HelpType, InsightModel, + InsightShortId, InsightType, - HelpType, - SessionRecordingUsageType, - FunnelCorrelation, ItemMode, - AnyPropertyFilter, - Experiment, - PropertyGroupFilter, - FilterLogicalOperator, + PersonType, PropertyFilterValue, - InsightShortId, - SessionPlayerData, - AnyPartialFilterType, - Resource, - AccessLevel, + PropertyGroupFilter, RecordingReportLoadTimes, + Resource, + SessionPlayerData, SessionRecordingPlayerTab, + SessionRecordingUsageType, Survey, } from '~/types' -import type { Dayjs } from 'lib/dayjs' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { now } from 'lib/dayjs' -import { - isFilterWithDisplay, - isFunnelsFilter, - isPathsFilter, - isRetentionFilter, - isStickinessFilter, - isTrendsFilter, -} from 'scenes/insights/sharedUtils' -import { convertPropertyGroupToProperties, isGroupPropertyFilter } from 'lib/components/PropertyFilters/utils' -import { EventIndex } from 'scenes/session-recordings/player/eventIndex' -import { SurveyTemplateType } from 'scenes/surveys/constants' + +import type { eventUsageLogicType } from './eventUsageLogicType' export enum DashboardEventSource { LongPress = 'long_press', @@ -171,9 +173,10 @@ function sanitizeFilterParams(filters: AnyPartialFilterType): Record([ posthog.capture('survey created', { name: survey.name, id: survey.id, + survey_type: survey.type, questions_length: survey.questions.length, question_types: survey.questions.map((question) => question.type), }) @@ -1109,6 +1113,7 @@ export const eventUsageLogic = kea([ posthog.capture('survey launched', { name: survey.name, id: survey.id, + survey_type: survey.type, question_types: survey.questions.map((question) => question.type), created_at: survey.created_at, start_date: survey.start_date, diff --git a/frontend/src/lib/utils/kea-logic-builders.ts b/frontend/src/lib/utils/kea-logic-builders.ts index 8724c0acfd9fb..92c4ff8fb7344 100644 --- a/frontend/src/lib/utils/kea-logic-builders.ts +++ b/frontend/src/lib/utils/kea-logic-builders.ts @@ -1,4 +1,4 @@ -import { BuiltLogic, afterMount } from 'kea' +import { afterMount, BuiltLogic } from 'kea' /** * Some kea logics are used heavily across multiple areas so we keep it mounted once loaded with this trick. */ diff --git a/frontend/src/lib/utils/logics.ts b/frontend/src/lib/utils/logics.ts index 5efc717ac4975..93efac6422618 100644 --- a/frontend/src/lib/utils/logics.ts +++ b/frontend/src/lib/utils/logics.ts @@ -1,4 +1,5 @@ import { organizationLogic } from 'scenes/organizationLogic' + import { teamLogic } from '../../scenes/teamLogic' import { OrganizationType, TeamType } from '../../types' import { getAppContext } from './getAppContext' diff --git a/frontend/src/loadPostHogJS.tsx b/frontend/src/loadPostHogJS.tsx index 2acd266241f82..807fce2883849 100644 --- a/frontend/src/loadPostHogJS.tsx +++ b/frontend/src/loadPostHogJS.tsx @@ -1,6 +1,6 @@ -import posthog, { PostHogConfig } from 'posthog-js' import * as Sentry from '@sentry/react' import { FEATURE_FLAGS } from 'lib/constants' +import posthog, { PostHogConfig } from 'posthog-js' const configWithSentry = (config: Partial): Partial => { if ((window as any).SENTRY_DSN) { @@ -27,8 +27,8 @@ export function loadPostHogJS(): void { bootstrap: window.POSTHOG_USER_IDENTITY_WITH_FLAGS ? window.POSTHOG_USER_IDENTITY_WITH_FLAGS : {}, opt_in_site_apps: true, loaded: (posthog) => { - if (posthog.webPerformance) { - posthog.webPerformance._forceAllowLocalhost = true + if (posthog.sessionRecording) { + posthog.sessionRecording._forceAllowLocalhostNetworkCapture = true } if (window.IMPERSONATED_SESSION) { diff --git a/frontend/src/mocks/browser.tsx b/frontend/src/mocks/browser.tsx index 6baf8552fa047..846e69bbf6e14 100644 --- a/frontend/src/mocks/browser.tsx +++ b/frontend/src/mocks/browser.tsx @@ -1,7 +1,8 @@ +import { DecoratorFunction } from '@storybook/types' import { rest, setupWorker } from 'msw' + import { handlers } from '~/mocks/handlers' import { Mocks, mocksToHandlers } from '~/mocks/utils' -import { DecoratorFunction } from '@storybook/types' // Default handlers ensure no request is unhandled by msw export const worker = setupWorker(...handlers) diff --git a/frontend/src/mocks/handlers.ts b/frontend/src/mocks/handlers.ts index 7b17d549ffc8a..7da9a39b575fb 100644 --- a/frontend/src/mocks/handlers.ts +++ b/frontend/src/mocks/handlers.ts @@ -1,20 +1,22 @@ -import { Mocks, MockSignature, mocksToHandlers } from './utils' import { + MOCK_DEFAULT_COHORT, MOCK_DEFAULT_ORGANIZATION, MOCK_DEFAULT_ORGANIZATION_INVITE, MOCK_DEFAULT_ORGANIZATION_MEMBER, + MOCK_DEFAULT_PLUGIN, + MOCK_DEFAULT_PLUGIN_CONFIG, MOCK_DEFAULT_TEAM, MOCK_DEFAULT_USER, - MOCK_DEFAULT_COHORT, MOCK_PERSON_PROPERTIES, - MOCK_DEFAULT_PLUGIN, - MOCK_DEFAULT_PLUGIN_CONFIG, - MOCK_TEAM_ID, MOCK_SECOND_ORGANIZATION_MEMBER, + MOCK_TEAM_ID, } from 'lib/api.mock' + import { getAvailableFeatures } from '~/mocks/features' import { SharingConfigurationType } from '~/types' +import { Mocks, MockSignature, mocksToHandlers } from './utils' + export const EMPTY_PAGINATED_RESPONSE = { count: 0, results: [] as any[], next: null, previous: null } export const toPaginatedResponse = (results: any[]): typeof EMPTY_PAGINATED_RESPONSE => ({ count: results.length, diff --git a/frontend/src/mocks/jest.ts b/frontend/src/mocks/jest.ts index 2a376fa4d6857..9023bc426af2f 100644 --- a/frontend/src/mocks/jest.ts +++ b/frontend/src/mocks/jest.ts @@ -1,7 +1,8 @@ import { setupServer } from 'msw/node' + +import { useAvailableFeatures } from '~/mocks/features' import { handlers } from '~/mocks/handlers' import { Mocks, mocksToHandlers } from '~/mocks/utils' -import { useAvailableFeatures } from '~/mocks/features' export const mswServer = setupServer(...handlers) export const useMocks = (mocks: Mocks): void => mswServer.use(...mocksToHandlers(mocks)) diff --git a/frontend/src/models/actionsModel.ts b/frontend/src/models/actionsModel.ts index 72735d29555b8..b6e508a3d7ed1 100644 --- a/frontend/src/models/actionsModel.ts +++ b/frontend/src/models/actionsModel.ts @@ -1,10 +1,12 @@ +import { connect, events, kea, path, props, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, path, connect, selectors, events } from 'kea' import api from 'lib/api' +import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' + import { ActionType } from '~/types' + import type { actionsModelType } from './actionsModelType' -import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' -import { permanentlyMount } from 'lib/utils/kea-logic-builders' export interface ActionsModelProps { params?: string diff --git a/frontend/src/models/annotationsModel.ts b/frontend/src/models/annotationsModel.ts index c22abb4754971..99fd787ac50c4 100644 --- a/frontend/src/models/annotationsModel.ts +++ b/frontend/src/models/annotationsModel.ts @@ -1,12 +1,14 @@ import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' -import api from 'lib/api' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import type { annotationsModelType } from './annotationsModelType' -import { RawAnnotationType, AnnotationType } from '~/types' import { loaders } from 'kea-loaders' -import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' +import api from 'lib/api' import { dayjsUtcToTimezone } from 'lib/dayjs' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' + +import { AnnotationType, RawAnnotationType } from '~/types' + +import type { annotationsModelType } from './annotationsModelType' export type AnnotationData = Pick export type AnnotationDataWithoutInsight = Omit diff --git a/frontend/src/models/cohortsModel.ts b/frontend/src/models/cohortsModel.ts index 13f994ab31b95..b095a9b946472 100644 --- a/frontend/src/models/cohortsModel.ts +++ b/frontend/src/models/cohortsModel.ts @@ -1,7 +1,14 @@ +import Fuse from 'fuse.js' +import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners, beforeUnmount, afterMount } from 'kea' import api from 'lib/api' -import type { cohortsModelType } from './cohortsModelType' +import { triggerExport } from 'lib/components/ExportButton/exporter' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types' +import { personsLogic } from 'scenes/persons/personsLogic' +import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' + import { AnyCohortCriteriaType, BehavioralCohortType, @@ -10,13 +17,8 @@ import { CohortType, ExporterFormat, } from '~/types' -import { personsLogic } from 'scenes/persons/personsLogic' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { triggerExport } from 'lib/components/ExportButton/exporter' -import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' -import Fuse from 'fuse.js' -import { permanentlyMount } from 'lib/utils/kea-logic-builders' -import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types' + +import type { cohortsModelType } from './cohortsModelType' const POLL_TIMEOUT = 5000 diff --git a/frontend/src/models/dashboardsModel.test.ts b/frontend/src/models/dashboardsModel.test.ts index dcbba56fe24b2..8c021609e5ded 100644 --- a/frontend/src/models/dashboardsModel.test.ts +++ b/frontend/src/models/dashboardsModel.test.ts @@ -1,9 +1,11 @@ -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' -import { DashboardBasicType } from '~/types' +import { DashboardPrivilegeLevel, DashboardRestrictionLevel } from 'lib/constants' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { DashboardBasicType } from '~/types' + import { dashboardsModel, nameCompareFunction } from './dashboardsModel' -import { DashboardPrivilegeLevel, DashboardRestrictionLevel } from 'lib/constants' const dashboards: Partial[] = [ { diff --git a/frontend/src/models/dashboardsModel.tsx b/frontend/src/models/dashboardsModel.tsx index 55bf7adc27e7d..54d52b20ad986 100644 --- a/frontend/src/models/dashboardsModel.tsx +++ b/frontend/src/models/dashboardsModel.tsx @@ -1,17 +1,19 @@ +import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners, afterMount } from 'kea' import { router, urlToAction } from 'kea-router' import api, { PaginatedResponse } from 'lib/api' +import { GENERATED_DASHBOARD_PREFIX } from 'lib/constants' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { idToKey, isUserLoggedIn } from 'lib/utils' import { DashboardEventSource, eventUsageLogic } from 'lib/utils/eventUsageLogic' -import type { dashboardsModelType } from './dashboardsModelType' -import { DashboardBasicType, DashboardTile, DashboardType, InsightModel, InsightShortId } from '~/types' -import { urls } from 'scenes/urls' +import { permanentlyMount } from 'lib/utils/kea-logic-builders' import { teamLogic } from 'scenes/teamLogic' -import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { urls } from 'scenes/urls' + import { tagsModel } from '~/models/tagsModel' -import { GENERATED_DASHBOARD_PREFIX } from 'lib/constants' -import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import { DashboardBasicType, DashboardTile, DashboardType, InsightModel, InsightShortId } from '~/types' + +import type { dashboardsModelType } from './dashboardsModelType' export const dashboardsModel = kea([ path(['models', 'dashboardsModel']), diff --git a/frontend/src/models/funnelsModel.ts b/frontend/src/models/funnelsModel.ts index 63aa6ec71f4d6..d07a155dbe873 100644 --- a/frontend/src/models/funnelsModel.ts +++ b/frontend/src/models/funnelsModel.ts @@ -1,10 +1,12 @@ +import { actions, events, kea, listeners, path, reducers } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, actions, reducers, listeners, events } from 'kea' import api from 'lib/api' import { toParams } from 'lib/utils' -import { SavedFunnel, InsightType } from '~/types' -import type { funnelsModelType } from './funnelsModelType' + +import { InsightType, SavedFunnel } from '~/types' + import { teamLogic } from '../scenes/teamLogic' +import type { funnelsModelType } from './funnelsModelType' const parseSavedFunnel = (result: Record): SavedFunnel => { return { diff --git a/frontend/src/models/groupPropertiesModel.ts b/frontend/src/models/groupPropertiesModel.ts index 067ff5ff8beba..3a5839146e186 100644 --- a/frontend/src/models/groupPropertiesModel.ts +++ b/frontend/src/models/groupPropertiesModel.ts @@ -1,10 +1,12 @@ +import { connect, events, kea, path, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, selectors, events } from 'kea' -import type { groupPropertiesModelType } from './groupPropertiesModelType' import api from 'lib/api' -import { GroupTypeProperties, PersonProperty } from '~/types' -import { teamLogic } from 'scenes/teamLogic' import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { GroupTypeProperties, PersonProperty } from '~/types' + +import type { groupPropertiesModelType } from './groupPropertiesModelType' export const groupPropertiesModel = kea([ path(['models', 'groupPropertiesModel']), diff --git a/frontend/src/models/groupsModel.ts b/frontend/src/models/groupsModel.ts index 1c5506f5c6c87..eb8babc316dc2 100644 --- a/frontend/src/models/groupsModel.ts +++ b/frontend/src/models/groupsModel.ts @@ -1,12 +1,14 @@ -import { kea, path, connect, selectors, afterMount } from 'kea' +import { afterMount, connect, kea, path, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' -import { GroupType, GroupTypeIndex } from '~/types' -import { teamLogic } from 'scenes/teamLogic' -import type { groupsModelType } from './groupsModelType' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { groupsAccessLogic, GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' -import { subscriptions } from 'kea-subscriptions' -import { loaders } from 'kea-loaders' +import { teamLogic } from 'scenes/teamLogic' + +import { GroupType, GroupTypeIndex } from '~/types' + +import type { groupsModelType } from './groupsModelType' export interface Noun { singular: string diff --git a/frontend/src/models/insightsModel.tsx b/frontend/src/models/insightsModel.tsx index 3be3e93faa231..02e0e5f21d588 100644 --- a/frontend/src/models/insightsModel.tsx +++ b/frontend/src/models/insightsModel.tsx @@ -1,10 +1,12 @@ -import { kea, path, connect, actions, listeners } from 'kea' +import { actions, connect, kea, listeners, path } from 'kea' import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { promptLogic } from 'lib/logic/promptLogic' -import { InsightModel } from '~/types' import { teamLogic } from 'scenes/teamLogic' + +import { InsightModel } from '~/types' + import type { insightsModelType } from './insightsModelType' -import { lemonToast } from 'lib/lemon-ui/lemonToast' export const insightsModel = kea([ path(['models', 'insightsModel']), diff --git a/frontend/src/models/notebooksModel.ts b/frontend/src/models/notebooksModel.ts index 70936e1c6c8bd..59dd745b092d0 100644 --- a/frontend/src/models/notebooksModel.ts +++ b/frontend/src/models/notebooksModel.ts @@ -1,23 +1,22 @@ import { actions, BuiltLogic, connect, kea, listeners, path, reducers } from 'kea' - import { loaders } from 'kea-loaders' -import { DashboardType, NotebookListItemType, NotebookNodeType, NotebookTarget } from '~/types' - +import { router } from 'kea-router' import api from 'lib/api' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' import posthog from 'posthog-js' +import { notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic' +import { notebookLogicType } from 'scenes/notebooks/Notebook/notebookLogicType' +import { defaultNotebookContent, EditorFocusPosition, JSONContent } from 'scenes/notebooks/Notebook/utils' +import { notebookPanelLogic } from 'scenes/notebooks/NotebookPanel/notebookPanelLogic' import { LOCAL_NOTEBOOK_TEMPLATES } from 'scenes/notebooks/NotebookTemplates/notebookTemplates' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' import { teamLogic } from 'scenes/teamLogic' -import { defaultNotebookContent, EditorFocusPosition, JSONContent } from 'scenes/notebooks/Notebook/utils' - -import type { notebooksModelType } from './notebooksModelType' -import { notebookLogicType } from 'scenes/notebooks/Notebook/notebookLogicType' import { urls } from 'scenes/urls' -import { notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic' -import { router } from 'kea-router' + import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' import { InsightVizNode, Node, NodeKind } from '~/queries/schema' -import { notebookPanelLogic } from 'scenes/notebooks/NotebookPanel/notebookPanelLogic' +import { DashboardType, NotebookListItemType, NotebookNodeType, NotebookTarget } from '~/types' + +import type { notebooksModelType } from './notebooksModelType' export const SCRATCHPAD_NOTEBOOK: NotebookListItemType = { short_id: 'scratchpad', diff --git a/frontend/src/models/propertyDefinitionsModel.test.ts b/frontend/src/models/propertyDefinitionsModel.test.ts index bf08b2bff4112..68e6177f98ba0 100644 --- a/frontend/src/models/propertyDefinitionsModel.test.ts +++ b/frontend/src/models/propertyDefinitionsModel.test.ts @@ -1,9 +1,10 @@ -import { initKeaTests } from '~/test/init' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' import { expectLogic, partial } from 'kea-test-utils' -import { PropertyDefinition, PropertyDefinitionState, PropertyDefinitionType, PropertyType } from '~/types' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' + import { useMocks } from '~/mocks/jest' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { initKeaTests } from '~/test/init' +import { PropertyDefinition, PropertyDefinitionState, PropertyDefinitionType, PropertyType } from '~/types' const propertyDefinitions: PropertyDefinition[] = [ { diff --git a/frontend/src/models/propertyDefinitionsModel.ts b/frontend/src/models/propertyDefinitionsModel.ts index b2dcb6cc2952d..7c4d55c4460e1 100644 --- a/frontend/src/models/propertyDefinitionsModel.ts +++ b/frontend/src/models/propertyDefinitionsModel.ts @@ -1,5 +1,12 @@ import { actions, kea, listeners, path, reducers, selectors } from 'kea' import api, { ApiMethodOptions } from 'lib/api' +import { TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' +import { dayjs } from 'lib/dayjs' +import { captureTimeToSeeData } from 'lib/internalMetrics' +import { colonDelimitedDuration } from 'lib/utils' +import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import { teamLogic } from 'scenes/teamLogic' + import { BreakdownKeyType, PropertyDefinition, @@ -8,13 +15,8 @@ import { PropertyFilterValue, PropertyType, } from '~/types' + import type { propertyDefinitionsModelType } from './propertyDefinitionsModelType' -import { dayjs } from 'lib/dayjs' -import { TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' -import { colonDelimitedDuration } from 'lib/utils' -import { captureTimeToSeeData } from 'lib/internalMetrics' -import { teamLogic } from 'scenes/teamLogic' -import { permanentlyMount } from 'lib/utils/kea-logic-builders' export type PropertyDefinitionStorage = Record diff --git a/frontend/src/models/tagsModel.ts b/frontend/src/models/tagsModel.ts index 1157e6029b4b1..e5a32d8e3c4c4 100644 --- a/frontend/src/models/tagsModel.ts +++ b/frontend/src/models/tagsModel.ts @@ -1,9 +1,9 @@ import { afterMount, connect, kea, path } from 'kea' +import { loaders } from 'kea-loaders' import api from 'lib/api' +import { organizationLogic } from 'scenes/organizationLogic' import type { tagsModelType } from './tagsModelType' -import { loaders } from 'kea-loaders' -import { organizationLogic } from 'scenes/organizationLogic' export const tagsModel = kea([ path(['models', 'tagsModel']), diff --git a/frontend/src/queries/Query/Query.tsx b/frontend/src/queries/Query/Query.tsx index 369eb6792fe59..f3e00c66cee67 100644 --- a/frontend/src/queries/Query/Query.tsx +++ b/frontend/src/queries/Query/Query.tsx @@ -1,3 +1,17 @@ +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { useEffect, useState } from 'react' + +import { ErrorBoundary } from '~/layout/ErrorBoundary' +import { DataNode } from '~/queries/nodes/DataNode/DataNode' +import { DataTable } from '~/queries/nodes/DataTable/DataTable' +import { InsightViz } from '~/queries/nodes/InsightViz/InsightViz' +import { WebOverview } from '~/queries/nodes/WebOverview/WebOverview' +import { QueryEditor } from '~/queries/QueryEditor/QueryEditor' +import { AnyResponseType, Node, QuerySchema } from '~/queries/schema' +import { QueryContext } from '~/queries/types' + +import { SavedInsight } from '../nodes/SavedInsight/SavedInsight' +import { TimeToSeeData } from '../nodes/TimeToSeeData/TimeToSeeData' import { isDataNode, isDataTableNode, @@ -6,19 +20,6 @@ import { isTimeToSeeDataSessionsNode, isWebOverviewQuery, } from '../utils' -import { DataTable } from '~/queries/nodes/DataTable/DataTable' -import { DataNode } from '~/queries/nodes/DataNode/DataNode' -import { InsightViz } from '~/queries/nodes/InsightViz/InsightViz' -import { AnyResponseType, Node, QuerySchema } from '~/queries/schema' -import { QueryContext } from '~/queries/types' - -import { ErrorBoundary } from '~/layout/ErrorBoundary' -import { useEffect, useState } from 'react' -import { TimeToSeeData } from '../nodes/TimeToSeeData/TimeToSeeData' -import { QueryEditor } from '~/queries/QueryEditor/QueryEditor' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { SavedInsight } from '../nodes/SavedInsight/SavedInsight' -import { WebOverview } from '~/queries/nodes/WebOverview/WebOverview' export interface QueryProps { /** An optional key to identify the query */ diff --git a/frontend/src/queries/QueryEditor/QueryEditor.tsx b/frontend/src/queries/QueryEditor/QueryEditor.tsx index 9d0990b64ce26..a32bd9162b540 100644 --- a/frontend/src/queries/QueryEditor/QueryEditor.tsx +++ b/frontend/src/queries/QueryEditor/QueryEditor.tsx @@ -1,13 +1,14 @@ -import { useActions, useValues } from 'kea' import { useMonaco } from '@monaco-editor/react' -import { useEffect, useState } from 'react' -import schema from '~/queries/schema.json' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { CodeEditor } from 'lib/components/CodeEditors' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { queryEditorLogic } from '~/queries/QueryEditor/queryEditorLogic' +import { useEffect, useState } from 'react' import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' -import clsx from 'clsx' + +import { queryEditorLogic } from '~/queries/QueryEditor/queryEditorLogic' +import schema from '~/queries/schema.json' import { QueryContext } from '~/queries/types' -import { CodeEditor } from 'lib/components/CodeEditors' export interface QueryEditorProps { query: string diff --git a/frontend/src/queries/QueryEditor/queryEditorLogic.ts b/frontend/src/queries/QueryEditor/queryEditorLogic.ts index f73a338dc72f7..585f1b84db761 100644 --- a/frontend/src/queries/QueryEditor/queryEditorLogic.ts +++ b/frontend/src/queries/QueryEditor/queryEditorLogic.ts @@ -1,9 +1,10 @@ import { actions, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' +import { lemonToast } from 'lib/lemon-ui/lemonToast' + +import { QueryEditorProps } from '~/queries/QueryEditor/QueryEditor' import { Node } from '~/queries/schema' import type { queryEditorLogicType } from './queryEditorLogicType' -import { QueryEditorProps } from '~/queries/QueryEditor/QueryEditor' -import { lemonToast } from 'lib/lemon-ui/lemonToast' function prettyJSON(source: string): string { try { diff --git a/frontend/src/queries/examples.ts b/frontend/src/queries/examples.ts index 13df18e2a5999..2485308f00a2a 100644 --- a/frontend/src/queries/examples.ts +++ b/frontend/src/queries/examples.ts @@ -1,4 +1,5 @@ // This file contains example queries, used in storybook and in the /query interface. +import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { ActionsNode, DataTableNode, @@ -26,7 +27,6 @@ import { PropertyOperator, StepOrderValue, } from '~/types' -import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' const Events: EventsQuery = { kind: NodeKind.EventsQuery, diff --git a/frontend/src/queries/hooks/useDebouncedQuery.ts b/frontend/src/queries/hooks/useDebouncedQuery.ts index 2464a96e19711..dfc35f3214254 100644 --- a/frontend/src/queries/hooks/useDebouncedQuery.ts +++ b/frontend/src/queries/hooks/useDebouncedQuery.ts @@ -1,6 +1,7 @@ -import { Node } from '~/queries/schema' import { useEffect, useRef, useState } from 'react' +import { Node } from '~/queries/schema' + export function useDebouncedQuery( query: T, setQuery: ((query: T) => void) | undefined, diff --git a/frontend/src/queries/nodes/DataNode/AutoLoad.tsx b/frontend/src/queries/nodes/DataNode/AutoLoad.tsx index fb1be33db264b..962ead9bda621 100644 --- a/frontend/src/queries/nodes/DataNode/AutoLoad.tsx +++ b/frontend/src/queries/nodes/DataNode/AutoLoad.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' import { useEffect } from 'react' +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' + export function AutoLoad(): JSX.Element { const { autoLoadToggled } = useValues(dataNodeLogic) const { startAutoLoad, stopAutoLoad, toggleAutoLoad } = useActions(dataNodeLogic) diff --git a/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx b/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx index e91daaf1ca310..7b20caa2b8037 100644 --- a/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx +++ b/frontend/src/queries/nodes/DataNode/DataNode.stories.tsx @@ -1,18 +1,20 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { examples } from '~/queries/examples' + import { mswDecorator } from '~/mocks/browser' +import { examples } from '~/queries/examples' +import { Query } from '~/queries/Query/Query' + import events from './__mocks__/EventsNode.json' import persons from './__mocks__/PersonsNode.json' -import { Query } from '~/queries/Query/Query' type Story = StoryObj const meta: Meta = { title: 'Queries/DataNode', component: Query, + tags: ['test-skip'], parameters: { layout: 'fullscreen', viewMode: 'story', - testOptions: { skip: true }, }, decorators: [ mswDecorator({ diff --git a/frontend/src/queries/nodes/DataNode/DataNode.tsx b/frontend/src/queries/nodes/DataNode/DataNode.tsx index aab8597c8f4e0..97e1155a2436f 100644 --- a/frontend/src/queries/nodes/DataNode/DataNode.tsx +++ b/frontend/src/queries/nodes/DataNode/DataNode.tsx @@ -1,11 +1,12 @@ +import { useValues } from 'kea' +import { CodeEditor } from 'lib/components/CodeEditors' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { useState } from 'react' import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' -import { AnyResponseType, DataNode as DataNodeType, DataTableNode } from '~/queries/schema' -import { useValues } from 'kea' + import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { OpenEditorButton } from '~/queries/nodes/Node/OpenEditorButton' -import { CodeEditor } from 'lib/components/CodeEditors' +import { AnyResponseType, DataNode as DataNodeType, DataTableNode } from '~/queries/schema' interface DataNodeProps { query: DataNodeType diff --git a/frontend/src/queries/nodes/DataNode/DateRange.tsx b/frontend/src/queries/nodes/DataNode/DateRange.tsx index f28699cb34146..cd2232e95bf9c 100644 --- a/frontend/src/queries/nodes/DataNode/DateRange.tsx +++ b/frontend/src/queries/nodes/DataNode/DateRange.tsx @@ -1,4 +1,5 @@ import { DateFilter } from 'lib/components/DateFilter/DateFilter' + import { DataNode, EventsQuery, HogQLQuery } from '~/queries/schema' import { isEventsQuery, isHogQLQuery } from '~/queries/utils' diff --git a/frontend/src/queries/nodes/DataNode/ElapsedTime.tsx b/frontend/src/queries/nodes/DataNode/ElapsedTime.tsx index f09ab068e5f14..29dabc5c645b6 100644 --- a/frontend/src/queries/nodes/DataNode/ElapsedTime.tsx +++ b/frontend/src/queries/nodes/DataNode/ElapsedTime.tsx @@ -1,8 +1,9 @@ +import clsx from 'clsx' import { useValues } from 'kea' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { useState } from 'react' import { Popover } from 'lib/lemon-ui/Popover' -import clsx from 'clsx' +import { useState } from 'react' + +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { QueryTiming } from '~/queries/schema' export interface TimingsProps { diff --git a/frontend/src/queries/nodes/DataNode/LoadNext.tsx b/frontend/src/queries/nodes/DataNode/LoadNext.tsx index d81fea67ff685..3c3f7ab1fc9fe 100644 --- a/frontend/src/queries/nodes/DataNode/LoadNext.tsx +++ b/frontend/src/queries/nodes/DataNode/LoadNext.tsx @@ -1,6 +1,7 @@ import { useActions, useValues } from 'kea' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { LemonButton } from 'lib/lemon-ui/LemonButton' + +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { DataNode } from '~/queries/schema' import { isPersonsNode, isPersonsQuery } from '~/queries/utils' diff --git a/frontend/src/queries/nodes/DataNode/Reload.tsx b/frontend/src/queries/nodes/DataNode/Reload.tsx index f9996e2de6c0c..0b4fffe862633 100644 --- a/frontend/src/queries/nodes/DataNode/Reload.tsx +++ b/frontend/src/queries/nodes/DataNode/Reload.tsx @@ -1,9 +1,10 @@ import { useActions, useValues } from 'kea' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconRefresh } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { Spinner } from 'lib/lemon-ui/Spinner' +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' + export function Reload(): JSX.Element { const { responseLoading } = useValues(dataNodeLogic) const { loadData, cancelQuery } = useActions(dataNodeLogic) diff --git a/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts b/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts index 9cf95eb03127d..55a417bdff91e 100644 --- a/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts +++ b/frontend/src/queries/nodes/DataNode/dataNodeLogic.queryCancellation.test.ts @@ -1,10 +1,11 @@ -import { initKeaTests } from '~/test/init' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { expectLogic } from 'kea-test-utils' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import * as libUtils from 'lib/utils' + +import { useMocks } from '~/mocks/jest' import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { NodeKind } from '~/queries/schema' -import { useMocks } from '~/mocks/jest' -import * as libUtils from 'lib/utils' +import { initKeaTests } from '~/test/init' const testUniqueKey = 'testUniqueKey' diff --git a/frontend/src/queries/nodes/DataNode/dataNodeLogic.test.ts b/frontend/src/queries/nodes/DataNode/dataNodeLogic.test.ts index ce40db3bace81..fbe91ff1a4653 100644 --- a/frontend/src/queries/nodes/DataNode/dataNodeLogic.test.ts +++ b/frontend/src/queries/nodes/DataNode/dataNodeLogic.test.ts @@ -1,8 +1,9 @@ -import { initKeaTests } from '~/test/init' import { expectLogic, partial } from 'kea-test-utils' + import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { NodeKind } from '~/queries/schema' import { query } from '~/queries/query' +import { NodeKind } from '~/queries/schema' +import { initKeaTests } from '~/test/init' jest.mock('~/queries/query', () => { return { diff --git a/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts b/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts index 94c2651343b31..3b8ad8e62c2de 100644 --- a/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts +++ b/frontend/src/queries/nodes/DataNode/dataNodeLogic.ts @@ -1,20 +1,33 @@ -import { dayjs } from 'lib/dayjs' +import clsx from 'clsx' +import equal from 'fast-deep-equal' import { + actions, + afterMount, + beforeUnmount, + connect, kea, + key, + listeners, path, props, - key, - afterMount, - selectors, propsChanged, reducers, - actions, - beforeUnmount, - listeners, - connect, + selectors, } from 'kea' import { loaders } from 'kea-loaders' -import type { dataNodeLogicType } from './dataNodeLogicType' +import { subscriptions } from 'kea-subscriptions' +import api, { ApiMethodOptions, getJSONOrThrow } from 'lib/api' +import { FEATURE_FLAGS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { objectsEqual, shouldCancelQuery, uuid } from 'lib/utils' +import { UNSAVED_INSIGHT_MIN_REFRESH_INTERVAL_MINUTES } from 'scenes/insights/insightLogic' +import { compareInsightQuery } from 'scenes/insights/utils/compareInsightQuery' +import { teamLogic } from 'scenes/teamLogic' +import { userLogic } from 'scenes/userLogic' + +import { removeExpressionComment } from '~/queries/nodes/DataTable/utils' +import { query } from '~/queries/query' import { AnyResponseType, DataNode, @@ -26,27 +39,16 @@ import { QueryResponse, QueryTiming, } from '~/queries/schema' -import { query } from '~/queries/query' import { - isInsightQueryNode, isEventsQuery, + isInsightQueryNode, isPersonsNode, - isQueryWithHogQLSupport, isPersonsQuery, + isQueryWithHogQLSupport, } from '~/queries/utils' -import { subscriptions } from 'kea-subscriptions' -import { objectsEqual, shouldCancelQuery, uuid } from 'lib/utils' -import clsx from 'clsx' -import api, { ApiMethodOptions, getJSONOrThrow } from 'lib/api' -import { removeExpressionComment } from '~/queries/nodes/DataTable/utils' -import { userLogic } from 'scenes/userLogic' -import { UNSAVED_INSIGHT_MIN_REFRESH_INTERVAL_MINUTES } from 'scenes/insights/insightLogic' -import { teamLogic } from 'scenes/teamLogic' -import equal from 'fast-deep-equal' + import { filtersToQueryNode } from '../InsightQuery/utils/filtersToQueryNode' -import { compareInsightQuery } from 'scenes/insights/utils/compareInsightQuery' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' +import type { dataNodeLogicType } from './dataNodeLogicType' export interface DataNodeLogicProps { key: string diff --git a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.tsx b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.tsx index 0670b5c664c28..80b9835e4109f 100644 --- a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.tsx +++ b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator.tsx @@ -1,28 +1,31 @@ import './ColumnConfigurator.scss' + +import { DndContext } from '@dnd-kit/core' +import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' +import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' import { BindLogic, useActions, useValues } from 'kea' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { dataTableLogic } from '~/queries/nodes/DataTable/dataTableLogic' +import { PropertyFilterIcon } from 'lib/components/PropertyFilters/components/PropertyFilterIcon' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { RestrictedArea, RestrictedComponentProps, RestrictionScope } from 'lib/components/RestrictedArea' +import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { TeamMembershipLevel } from 'lib/constants' import { IconClose, IconEdit, IconTuning, SortableDragIcon } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' +import { LemonModal } from 'lib/lemon-ui/LemonModal' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { useState } from 'react' -import { columnConfiguratorLogic, ColumnConfiguratorLogicProps } from './columnConfiguratorLogic' -import { defaultDataTableColumns, extractExpressionComment, removeExpressionComment } from '../utils' +import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' + +import { dataTableLogic } from '~/queries/nodes/DataTable/dataTableLogic' import { DataTableNode, NodeKind } from '~/queries/schema' -import { LemonModal } from 'lib/lemon-ui/LemonModal' import { isEventsQuery, taxonomicEventFilterToHogQL, trimQuotes } from '~/queries/utils' -import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { PropertyFilterIcon } from 'lib/components/PropertyFilters/components/PropertyFilterIcon' import { PropertyFilterType } from '~/types' -import { TeamMembershipLevel } from 'lib/constants' -import { RestrictedArea, RestrictedComponentProps, RestrictionScope } from 'lib/components/RestrictedArea' -import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' -import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' -import { DndContext } from '@dnd-kit/core' -import { CSS } from '@dnd-kit/utilities' + +import { defaultDataTableColumns, extractExpressionComment, removeExpressionComment } from '../utils' +import { columnConfiguratorLogic, ColumnConfiguratorLogicProps } from './columnConfiguratorLogic' let uniqueNode = 0 diff --git a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.test.ts b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.test.ts index 66535ef3b3194..234e0b9d7a8ab 100644 --- a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.test.ts +++ b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.test.ts @@ -1,7 +1,9 @@ -import { columnConfiguratorLogic } from './columnConfiguratorLogic' import { expectLogic } from 'kea-test-utils' + import { initKeaTests } from '~/test/init' +import { columnConfiguratorLogic } from './columnConfiguratorLogic' + describe('columnConfiguratorLogic', () => { let logic: ReturnType diff --git a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.tsx b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.tsx index ee60a269a7968..8eac6b894c9e0 100644 --- a/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.tsx +++ b/frontend/src/queries/nodes/DataTable/ColumnConfigurator/columnConfiguratorLogic.tsx @@ -1,8 +1,10 @@ import { actions, kea, key, listeners, path, props, propsChanged, reducers } from 'kea' -import type { columnConfiguratorLogicType } from './columnConfiguratorLogicType' import { teamLogic } from 'scenes/teamLogic' + import { HOGQL_COLUMNS_KEY } from '~/queries/nodes/DataTable/defaultEventsQuery' +import type { columnConfiguratorLogicType } from './columnConfiguratorLogicType' + export interface ColumnConfiguratorLogicProps { key: string columns: string[] diff --git a/frontend/src/queries/nodes/DataTable/DataTable.examples.ts b/frontend/src/queries/nodes/DataTable/DataTable.examples.ts index 1c99e52230f42..6de2be61de591 100644 --- a/frontend/src/queries/nodes/DataTable/DataTable.examples.ts +++ b/frontend/src/queries/nodes/DataTable/DataTable.examples.ts @@ -1,6 +1,6 @@ +import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { DataTableNode, NodeKind, PersonsNode } from '~/queries/schema' import { PropertyFilterType, PropertyOperator } from '~/types' -import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' const AllDefaults: DataTableNode = { kind: NodeKind.DataTableNode, diff --git a/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx b/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx index 60072dd702ddb..64a978e45f95d 100644 --- a/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx +++ b/frontend/src/queries/nodes/DataTable/DataTable.stories.tsx @@ -1,18 +1,20 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { Query } from '~/queries/Query/Query' -import { examples } from './DataTable.examples' + import { mswDecorator } from '~/mocks/browser' +import { Query } from '~/queries/Query/Query' + import events from '../DataNode/__mocks__/EventsNode.json' import persons from '../DataNode/__mocks__/PersonsNode.json' +import { examples } from './DataTable.examples' type Story = StoryObj const meta: Meta = { title: 'Queries/DataTable', component: Query, + tags: ['test-skip'], parameters: { layout: 'fullscreen', viewMode: 'story', - testOptions: { skip: true }, }, decorators: [ mswDecorator({ diff --git a/frontend/src/queries/nodes/DataTable/DataTable.tsx b/frontend/src/queries/nodes/DataTable/DataTable.tsx index a3942c2c5c459..b814b6112fb13 100644 --- a/frontend/src/queries/nodes/DataTable/DataTable.tsx +++ b/frontend/src/queries/nodes/DataTable/DataTable.tsx @@ -1,4 +1,44 @@ import './DataTable.scss' + +import clsx from 'clsx' +import { BindLogic, useValues } from 'kea' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' +import { useCallback, useState } from 'react' +import { EventDetails } from 'scenes/events/EventDetails' +import { InsightEmptyState, InsightErrorState } from 'scenes/insights/EmptyStates' +import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal' +import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' + +import { AutoLoad } from '~/queries/nodes/DataNode/AutoLoad' +import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' +import { DateRange } from '~/queries/nodes/DataNode/DateRange' +import { ElapsedTime } from '~/queries/nodes/DataNode/ElapsedTime' +import { LoadNext } from '~/queries/nodes/DataNode/LoadNext' +import { Reload } from '~/queries/nodes/DataNode/Reload' +import { ColumnConfigurator } from '~/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator' +import { DataTableExport } from '~/queries/nodes/DataTable/DataTableExport' +import { dataTableLogic, DataTableLogicProps, DataTableRow } from '~/queries/nodes/DataTable/dataTableLogic' +import { EventRowActions } from '~/queries/nodes/DataTable/EventRowActions' +import { QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' +import { renderColumn } from '~/queries/nodes/DataTable/renderColumn' +import { renderColumnMeta } from '~/queries/nodes/DataTable/renderColumnMeta' +import { SavedQueries } from '~/queries/nodes/DataTable/SavedQueries' +import { + extractExpressionComment, + getDataNodeDefaultColumns, + removeExpressionComment, +} from '~/queries/nodes/DataTable/utils' +import { EventName } from '~/queries/nodes/EventsNode/EventName' +import { EventPropertyFilters } from '~/queries/nodes/EventsNode/EventPropertyFilters' +import { HogQLQueryEditor } from '~/queries/nodes/HogQLQuery/HogQLQueryEditor' +import { EditHogQLButton } from '~/queries/nodes/Node/EditHogQLButton' +import { OpenEditorButton } from '~/queries/nodes/Node/OpenEditorButton' +import { PersonPropertyFilters } from '~/queries/nodes/PersonsNode/PersonPropertyFilters' +import { PersonsSearch } from '~/queries/nodes/PersonsNode/PersonsSearch' import { AnyResponseType, DataTableNode, @@ -9,27 +49,6 @@ import { PersonsQuery, } from '~/queries/schema' import { QueryContext } from '~/queries/types' - -import { useCallback, useState } from 'react' -import { BindLogic, useValues } from 'kea' -import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' -import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { EventName } from '~/queries/nodes/EventsNode/EventName' -import { EventPropertyFilters } from '~/queries/nodes/EventsNode/EventPropertyFilters' -import { EventDetails } from 'scenes/events/EventDetails' -import { EventRowActions } from '~/queries/nodes/DataTable/EventRowActions' -import { DataTableExport } from '~/queries/nodes/DataTable/DataTableExport' -import { Reload } from '~/queries/nodes/DataNode/Reload' -import { LoadNext } from '~/queries/nodes/DataNode/LoadNext' -import { renderColumnMeta } from '~/queries/nodes/DataTable/renderColumnMeta' -import { renderColumn } from '~/queries/nodes/DataTable/renderColumn' -import { AutoLoad } from '~/queries/nodes/DataNode/AutoLoad' -import { dataTableLogic, DataTableLogicProps, DataTableRow } from '~/queries/nodes/DataTable/dataTableLogic' -import { ColumnConfigurator } from '~/queries/nodes/DataTable/ColumnConfigurator/ColumnConfigurator' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import clsx from 'clsx' -import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' -import { OpenEditorButton } from '~/queries/nodes/Node/OpenEditorButton' import { isEventsQuery, isHogQlAggregation, @@ -38,25 +57,7 @@ import { taxonomicEventFilterToHogQL, taxonomicPersonFilterToHogQL, } from '~/queries/utils' -import { PersonPropertyFilters } from '~/queries/nodes/PersonsNode/PersonPropertyFilters' -import { PersonsSearch } from '~/queries/nodes/PersonsNode/PersonsSearch' -import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal' -import { ElapsedTime } from '~/queries/nodes/DataNode/ElapsedTime' -import { DateRange } from '~/queries/nodes/DataNode/DateRange' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { - extractExpressionComment, - getDataNodeDefaultColumns, - removeExpressionComment, -} from '~/queries/nodes/DataTable/utils' -import { InsightEmptyState, InsightErrorState } from 'scenes/insights/EmptyStates' import { EventType } from '~/types' -import { SavedQueries } from '~/queries/nodes/DataTable/SavedQueries' -import { HogQLQueryEditor } from '~/queries/nodes/HogQLQuery/HogQLQueryEditor' -import { QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' -import { EditHogQLButton } from '~/queries/nodes/Node/EditHogQLButton' interface DataTableProps { uniqueKey?: string | number diff --git a/frontend/src/queries/nodes/DataTable/DataTableExport.tsx b/frontend/src/queries/nodes/DataTable/DataTableExport.tsx index 6965548e4afac..a1b04d60e6579 100644 --- a/frontend/src/queries/nodes/DataTable/DataTableExport.tsx +++ b/frontend/src/queries/nodes/DataTable/DataTableExport.tsx @@ -1,23 +1,25 @@ -import Papa from 'papaparse' -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { IconExport } from 'lib/lemon-ui/icons' +import { LemonDivider, lemonToast } from '@posthog/lemon-ui' +import { useValues } from 'kea' import { triggerExport } from 'lib/components/ExportButton/exporter' -import { ExporterFormat } from '~/types' -import { DataNode, DataTableNode, NodeKind } from '~/queries/schema' +import { IconExport } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import Papa from 'papaparse' +import { asDisplay } from 'scenes/persons/person-utils' +import { urls } from 'scenes/urls' + +import { ExportWithConfirmation } from '~/queries/nodes/DataTable/ExportWithConfirmation' import { defaultDataTableColumns, extractExpressionComment, removeExpressionComment, } from '~/queries/nodes/DataTable/utils' -import { isEventsQuery, isHogQLQuery, isPersonsNode } from '~/queries/utils' import { getPersonsEndpoint } from '~/queries/query' -import { ExportWithConfirmation } from '~/queries/nodes/DataTable/ExportWithConfirmation' -import { DataTableRow, dataTableLogic } from './dataTableLogic' -import { useValues } from 'kea' -import { LemonDivider, lemonToast } from '@posthog/lemon-ui' -import { asDisplay } from 'scenes/persons/person-utils' -import { urls } from 'scenes/urls' -import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { DataNode, DataTableNode, NodeKind } from '~/queries/schema' +import { isEventsQuery, isHogQLQuery, isPersonsNode } from '~/queries/utils' +import { ExporterFormat } from '~/types' + +import { dataTableLogic, DataTableRow } from './dataTableLogic' const EXPORT_MAX_LIMIT = 10000 diff --git a/frontend/src/queries/nodes/DataTable/EventRowActions.tsx b/frontend/src/queries/nodes/DataTable/EventRowActions.tsx index bcf63f7cb7eff..405691e4bcead 100644 --- a/frontend/src/queries/nodes/DataTable/EventRowActions.tsx +++ b/frontend/src/queries/nodes/DataTable/EventRowActions.tsx @@ -1,16 +1,17 @@ -import { EventType } from '~/types' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { createActionFromEvent } from 'scenes/events/createActionFromEvent' -import { urls } from 'scenes/urls' -import { getCurrentTeamId } from 'lib/utils/logics' -import { teamLogic } from 'scenes/teamLogic' -import { IconLink, IconPlayCircle } from 'lib/lemon-ui/icons' import { useActions } from 'kea' -import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic' -import { copyToClipboard } from 'lib/utils/copyToClipboard' import { dayjs } from 'lib/dayjs' +import { IconLink, IconPlayCircle } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { getCurrentTeamId } from 'lib/utils/logics' +import { createActionFromEvent } from 'scenes/events/createActionFromEvent' import { insightUrlForEvent } from 'scenes/insights/utils' +import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { EventType } from '~/types' interface EventActionProps { event: EventType diff --git a/frontend/src/queries/nodes/DataTable/SavedQueries.tsx b/frontend/src/queries/nodes/DataTable/SavedQueries.tsx index 15dc608ebf9ad..84e32452258f3 100644 --- a/frontend/src/queries/nodes/DataTable/SavedQueries.tsx +++ b/frontend/src/queries/nodes/DataTable/SavedQueries.tsx @@ -1,9 +1,10 @@ -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { DataTableNode } from '~/queries/schema' import equal from 'fast-deep-equal' import { useValues } from 'kea' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' import { teamLogic } from 'scenes/teamLogic' + import { getEventsQueriesForTeam } from '~/queries/nodes/DataTable/defaultEventsQuery' +import { DataTableNode } from '~/queries/schema' interface SavedQueriesProps { query: DataTableNode diff --git a/frontend/src/queries/nodes/DataTable/dataTableLogic.test.ts b/frontend/src/queries/nodes/DataTable/dataTableLogic.test.ts index bc87b07c458b4..d37c26c2df1c9 100644 --- a/frontend/src/queries/nodes/DataTable/dataTableLogic.test.ts +++ b/frontend/src/queries/nodes/DataTable/dataTableLogic.test.ts @@ -1,10 +1,11 @@ -import { initKeaTests } from '~/test/init' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { expectLogic, partial } from 'kea-test-utils' -import { dataTableLogic } from '~/queries/nodes/DataTable/dataTableLogic' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' + import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { DataTableNode, NodeKind } from '~/queries/schema' +import { dataTableLogic } from '~/queries/nodes/DataTable/dataTableLogic' import { query } from '~/queries/query' +import { DataTableNode, NodeKind } from '~/queries/schema' +import { initKeaTests } from '~/test/init' jest.mock('~/queries/query') diff --git a/frontend/src/queries/nodes/DataTable/dataTableLogic.ts b/frontend/src/queries/nodes/DataTable/dataTableLogic.ts index cb4463a1ee400..264f7145e50e8 100644 --- a/frontend/src/queries/nodes/DataTable/dataTableLogic.ts +++ b/frontend/src/queries/nodes/DataTable/dataTableLogic.ts @@ -1,5 +1,12 @@ +import equal from 'fast-deep-equal' import { actions, connect, kea, key, path, props, propsChanged, reducers, selectors } from 'kea' -import type { dataTableLogicType } from './dataTableLogicType' +import { FEATURE_FLAGS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { objectsEqual, sortedKeys } from 'lib/utils' + +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' +import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' import { AnyDataNode, DataTableNode, @@ -9,15 +16,10 @@ import { TimeToSeeDataSessionsQuery, } from '~/queries/schema' import { QueryContext } from '~/queries/types' -import { getColumnsForQuery, removeExpressionComment } from './utils' -import { objectsEqual, sortedKeys } from 'lib/utils' import { isDataTableNode, isEventsQuery } from '~/queries/utils' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { dayjs } from 'lib/dayjs' -import equal from 'fast-deep-equal' -import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' + +import type { dataTableLogicType } from './dataTableLogicType' +import { getColumnsForQuery, removeExpressionComment } from './utils' export interface DataTableLogicProps { vizKey: string diff --git a/frontend/src/queries/nodes/DataTable/defaultEventsQuery.ts b/frontend/src/queries/nodes/DataTable/defaultEventsQuery.ts index 4820fe7d578c4..8b027b285b4fc 100644 --- a/frontend/src/queries/nodes/DataTable/defaultEventsQuery.ts +++ b/frontend/src/queries/nodes/DataTable/defaultEventsQuery.ts @@ -1,7 +1,8 @@ -import { TeamType } from '~/types' -import { EventsQuery, NodeKind } from '~/queries/schema' import { getDefaultEventsSceneQuery } from 'scenes/events/defaults' + +import { EventsQuery, NodeKind } from '~/queries/schema' import { escapePropertyAsHogQlIdentifier } from '~/queries/utils' +import { TeamType } from '~/types' /** Indicates HogQL usage if team.live_events_columns = [HOGQL_COLUMNS_KEY, ...] */ export const HOGQL_COLUMNS_KEY = '--v2:hogql' diff --git a/frontend/src/queries/nodes/DataTable/queryFeatures.ts b/frontend/src/queries/nodes/DataTable/queryFeatures.ts index 8fe11b2b7aae6..4c2b4202ea539 100644 --- a/frontend/src/queries/nodes/DataTable/queryFeatures.ts +++ b/frontend/src/queries/nodes/DataTable/queryFeatures.ts @@ -1,3 +1,4 @@ +import { Node } from '~/queries/schema' import { isEventsQuery, isHogQLQuery, @@ -7,7 +8,6 @@ import { isWebStatsTableQuery, isWebTopClicksQuery, } from '~/queries/utils' -import { Node } from '~/queries/schema' export enum QueryFeature { columnsInResponse, diff --git a/frontend/src/queries/nodes/DataTable/renderColumn.tsx b/frontend/src/queries/nodes/DataTable/renderColumn.tsx index 7333f6f42fcca..846283c4cbfbb 100644 --- a/frontend/src/queries/nodes/DataTable/renderColumn.tsx +++ b/frontend/src/queries/nodes/DataTable/renderColumn.tsx @@ -1,14 +1,22 @@ -import { AnyPropertyFilter, EventType, PersonType, PropertyFilterType, PropertyOperator } from '~/types' -import { autoCaptureEventToDescription } from 'lib/utils' +import ReactJson from '@microlink/react-json-view' +import { combineUrl, router } from 'kea-router' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { Property } from 'lib/components/Property' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { Link } from 'lib/lemon-ui/Link' import { TZLabel } from 'lib/components/TZLabel' -import { Property } from 'lib/components/Property' -import { urls } from 'scenes/urls' +import { TableCellSparkline } from 'lib/lemon-ui/LemonTable/TableCellSparkline' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { Link } from 'lib/lemon-ui/Link' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { autoCaptureEventToDescription } from 'lib/utils' import { PersonDisplay, PersonDisplayProps } from 'scenes/persons/PersonDisplay' +import { urls } from 'scenes/urls' + +import { errorColumn, loadingColumn } from '~/queries/nodes/DataTable/dataTableLogic' +import { DeletePersonButton } from '~/queries/nodes/PersonsNode/DeletePersonButton' import { DataTableNode, EventsQueryPersonColumn, HasPropertiesNode } from '~/queries/schema' import { QueryContext } from '~/queries/types' - import { isEventsQuery, isHogQLQuery, @@ -17,15 +25,7 @@ import { isTimeToSeeDataSessionsQuery, trimQuotes, } from '~/queries/utils' -import { combineUrl, router } from 'kea-router' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { DeletePersonButton } from '~/queries/nodes/PersonsNode/DeletePersonButton' -import ReactJson from '@microlink/react-json-view' -import { errorColumn, loadingColumn } from '~/queries/nodes/DataTable/dataTableLogic' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { TableCellSparkline } from 'lib/lemon-ui/LemonTable/TableCellSparkline' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { AnyPropertyFilter, EventType, PersonType, PropertyFilterType, PropertyOperator } from '~/types' export function renderColumn( key: string, diff --git a/frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx b/frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx index f73466611bad4..06d31f17df12f 100644 --- a/frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx +++ b/frontend/src/queries/nodes/DataTable/renderColumnMeta.tsx @@ -1,12 +1,12 @@ -import { PropertyFilterType } from '~/types' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting' + +import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' +import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' import { DataTableNode, EventsQuery } from '~/queries/schema' import { QueryContext } from '~/queries/types' - import { isHogQLQuery, trimQuotes } from '~/queries/utils' -import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' -import { SortingIndicator } from 'lib/lemon-ui/LemonTable/sorting' -import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' +import { PropertyFilterType } from '~/types' export interface ColumnMeta { title?: JSX.Element | string diff --git a/frontend/src/queries/nodes/DataTable/utils.ts b/frontend/src/queries/nodes/DataTable/utils.ts index 94c196c2afe07..64ab750ec8b00 100644 --- a/frontend/src/queries/nodes/DataTable/utils.ts +++ b/frontend/src/queries/nodes/DataTable/utils.ts @@ -1,5 +1,5 @@ -import { DataNode, DataTableNode, EventsQuery, HogQLExpression, NodeKind } from '~/queries/schema' import { getQueryFeatures, QueryFeature } from '~/queries/nodes/DataTable/queryFeatures' +import { DataNode, DataTableNode, EventsQuery, HogQLExpression, NodeKind } from '~/queries/schema' export const defaultDataTableEventColumns: HogQLExpression[] = [ '*', diff --git a/frontend/src/queries/nodes/EventsNode/EventName.tsx b/frontend/src/queries/nodes/EventsNode/EventName.tsx index 50351d57eaf3c..a6cb81111d20c 100644 --- a/frontend/src/queries/nodes/EventsNode/EventName.tsx +++ b/frontend/src/queries/nodes/EventsNode/EventName.tsx @@ -1,6 +1,7 @@ -import { EventsNode, EventsQuery } from '~/queries/schema' import { LemonEventName } from 'scenes/actions/EventName' +import { EventsNode, EventsQuery } from '~/queries/schema' + interface EventNameProps { query: EventsNode | EventsQuery setQuery?: (query: EventsNode | EventsQuery) => void diff --git a/frontend/src/queries/nodes/EventsNode/EventPropertyFilters.tsx b/frontend/src/queries/nodes/EventsNode/EventPropertyFilters.tsx index d7dc068310111..6337cd2e474a7 100644 --- a/frontend/src/queries/nodes/EventsNode/EventPropertyFilters.tsx +++ b/frontend/src/queries/nodes/EventsNode/EventPropertyFilters.tsx @@ -1,9 +1,10 @@ -import { EventsNode, EventsQuery, HogQLQuery } from '~/queries/schema' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { AnyPropertyFilter } from '~/types' -import { useState } from 'react' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { useState } from 'react' + +import { EventsNode, EventsQuery, HogQLQuery } from '~/queries/schema' import { isHogQLQuery } from '~/queries/utils' +import { AnyPropertyFilter } from '~/types' interface EventPropertyFiltersProps { query: EventsNode | EventsQuery | HogQLQuery diff --git a/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx b/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx index e8e8c16035ea4..bc7bb2e4f8bfd 100644 --- a/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx +++ b/frontend/src/queries/nodes/HogQLQuery/HogQLQueryEditor.tsx @@ -1,19 +1,21 @@ -import { useActions, useValues } from 'kea' -import { HogQLQuery } from '~/queries/schema' -import { useEffect, useRef, useState } from 'react' -import { hogQLQueryEditorLogic } from './hogQLQueryEditorLogic' import { Monaco } from '@monaco-editor/react' -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { IconAutoAwesome, IconInfo } from 'lib/lemon-ui/icons' import { LemonInput, Link } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' -import type { IDisposable, editor as importedEditor, languages } from 'monaco-editor' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { CodeEditor } from 'lib/components/CodeEditors' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FEATURE_FLAGS } from 'lib/constants' +import { IconAutoAwesome, IconInfo } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { CodeEditor } from 'lib/components/CodeEditors' -import clsx from 'clsx' +import type { editor as importedEditor, IDisposable, languages } from 'monaco-editor' +import { useEffect, useRef, useState } from 'react' +import { urls } from 'scenes/urls' + +import { HogQLQuery } from '~/queries/schema' + +import { hogQLQueryEditorLogic } from './hogQLQueryEditorLogic' export interface HogQLQueryEditorProps { query: HogQLQuery diff --git a/frontend/src/queries/nodes/HogQLQuery/hogQLQueryEditorLogic.ts b/frontend/src/queries/nodes/HogQLQuery/hogQLQueryEditorLogic.ts index 31d49a241a5e5..f64615df762e2 100644 --- a/frontend/src/queries/nodes/HogQLQuery/hogQLQueryEditorLogic.ts +++ b/frontend/src/queries/nodes/HogQLQuery/hogQLQueryEditorLogic.ts @@ -1,6 +1,8 @@ +import type { Monaco } from '@monaco-editor/react' import { actions, connect, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' -import { HogQLMetadata, HogQLNotice, HogQLQuery, NodeKind } from '~/queries/schema' -import type { hogQLQueryEditorLogicType } from './hogQLQueryEditorLogicType' +import { combineUrl } from 'kea-router' +import api from 'lib/api' +import { promptLogic } from 'lib/logic/promptLogic' // Note: we can oly import types and not values from monaco-editor, because otherwise some Monaco code breaks // auto reload in development. Specifically, on this line: // `export const suggestWidgetStatusbarMenu = new MenuId('suggestWidgetStatusBar')` @@ -9,13 +11,13 @@ import type { hogQLQueryEditorLogicType } from './hogQLQueryEditorLogicType' // esbuild doesn't support manual chunks as of 2023, so we can't just put Monaco in its own chunk, which would prevent // re-importing. As for @monaco-editor/react, it does some lazy loading and doesn't have this problem. import type { editor, MarkerSeverity } from 'monaco-editor' -import { query } from '~/queries/query' -import type { Monaco } from '@monaco-editor/react' -import api from 'lib/api' -import { combineUrl } from 'kea-router' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { dataWarehouseSavedQueriesLogic } from 'scenes/data-warehouse/saved_queries/dataWarehouseSavedQueriesLogic' -import { promptLogic } from 'lib/logic/promptLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { query } from '~/queries/query' +import { HogQLMetadata, HogQLNotice, HogQLQuery, NodeKind } from '~/queries/schema' + +import type { hogQLQueryEditorLogicType } from './hogQLQueryEditorLogicType' export interface ModelMarker extends editor.IMarkerData { hogQLFix?: string diff --git a/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.test.ts b/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.test.ts index 30af701ae91a1..ac9ebba3bc553 100644 --- a/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.test.ts +++ b/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.test.ts @@ -1,40 +1,42 @@ import { FunnelLayout, ShownAsValue } from 'lib/constants' + import { - InsightQueryNode, - TrendsQuery, FunnelsQuery, - RetentionQuery, - StickinessQuery, + InsightQueryNode, LifecycleQuery, NodeKind, PathsQuery, + RetentionQuery, + StickinessQuery, + TrendsQuery, } from '~/queries/schema' import { - TrendsFilterType, - RetentionFilterType, - FunnelsFilterType, - PathsFilterType, - StickinessFilterType, - LifecycleFilterType, ActionFilter, BaseMathType, + BreakdownAttributionType, ChartDisplayType, FilterLogicalOperator, FilterType, + FunnelConversionWindowTimeUnit, + FunnelPathType, + FunnelsFilterType, + FunnelStepReference, + FunnelVizType, + GroupMathType, InsightType, + LifecycleFilterType, + PathsFilterType, + PathType, PropertyFilterType, PropertyMathType, PropertyOperator, - FunnelVizType, - FunnelStepReference, - BreakdownAttributionType, - FunnelConversionWindowTimeUnit, - StepOrderValue, - PathType, - FunnelPathType, + RetentionFilterType, RetentionPeriod, - GroupMathType, + StepOrderValue, + StickinessFilterType, + TrendsFilterType, } from '~/types' + import { actionsAndEventsToSeries, cleanHiddenLegendIndexes, diff --git a/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.ts b/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.ts index 93f5339cc929b..3b0f0fad4aadb 100644 --- a/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.ts +++ b/frontend/src/queries/nodes/InsightQuery/utils/filtersToQueryNode.ts @@ -1,33 +1,34 @@ +import * as Sentry from '@sentry/react' +import { objectCleanWithEmpty } from 'lib/utils' +import { transformLegacyHiddenLegendKeys } from 'scenes/funnels/funnelUtils' +import { + isFunnelsFilter, + isLifecycleFilter, + isPathsFilter, + isRetentionFilter, + isStickinessFilter, + isTrendsFilter, +} from 'scenes/insights/sharedUtils' + import { - InsightQueryNode, - EventsNode, ActionsNode, - NodeKind, + EventsNode, InsightNodeKind, + InsightQueryNode, InsightsQueryBase, + NodeKind, } from '~/queries/schema' -import { FilterType, InsightType, ActionFilter } from '~/types' import { - isTrendsQuery, isFunnelsQuery, - isRetentionQuery, - isPathsQuery, - isStickinessQuery, - isLifecycleQuery, isInsightQueryWithBreakdown, isInsightQueryWithSeries, + isLifecycleQuery, + isPathsQuery, + isRetentionQuery, + isStickinessQuery, + isTrendsQuery, } from '~/queries/utils' -import { - isTrendsFilter, - isFunnelsFilter, - isRetentionFilter, - isPathsFilter, - isStickinessFilter, - isLifecycleFilter, -} from 'scenes/insights/sharedUtils' -import { objectCleanWithEmpty } from 'lib/utils' -import { transformLegacyHiddenLegendKeys } from 'scenes/funnels/funnelUtils' -import * as Sentry from '@sentry/react' +import { ActionFilter, FilterType, InsightType } from '~/types' const reverseInsightMap: Record, InsightNodeKind> = { [InsightType.TRENDS]: NodeKind.TrendsQuery, diff --git a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.test.ts b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.test.ts index e6d9bb0eccdb5..eca66074d0f9f 100644 --- a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.test.ts +++ b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.test.ts @@ -1,6 +1,6 @@ import { hiddenLegendItemsToKeys, queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { InsightType, LifecycleFilterType } from '~/types' import { LifecycleQuery, NodeKind } from '~/queries/schema' +import { InsightType, LifecycleFilterType } from '~/types' describe('queryNodeToFilter', () => { test('converts a query node to a filter', () => { diff --git a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts index 03d904476be9a..cd6a6a2848b9a 100644 --- a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts +++ b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts @@ -1,5 +1,7 @@ +import { objectClean } from 'lib/utils' +import { isFunnelsFilter, isLifecycleFilter, isStickinessFilter, isTrendsFilter } from 'scenes/insights/sharedUtils' + import { ActionsNode, BreakdownFilter, EventsNode, InsightNodeKind, InsightQueryNode, NodeKind } from '~/queries/schema' -import { ActionFilter, EntityTypes, FilterType, InsightType } from '~/types' import { isActionsNode, isEventsNode, @@ -10,8 +12,7 @@ import { isStickinessQuery, isTrendsQuery, } from '~/queries/utils' -import { objectClean } from 'lib/utils' -import { isFunnelsFilter, isLifecycleFilter, isStickinessFilter, isTrendsFilter } from 'scenes/insights/sharedUtils' +import { ActionFilter, EntityTypes, FilterType, InsightType } from '~/types' type FilterTypeActionsAndEvents = { events?: ActionFilter[]; actions?: ActionFilter[]; new_entity?: ActionFilter[] } diff --git a/frontend/src/queries/nodes/InsightViz/Breakdown.tsx b/frontend/src/queries/nodes/InsightViz/Breakdown.tsx index 9366b7b2ff2a6..c1aa833f34910 100644 --- a/frontend/src/queries/nodes/InsightViz/Breakdown.tsx +++ b/frontend/src/queries/nodes/InsightViz/Breakdown.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' -import { EditorFilterProps } from '~/types' import { TaxonomicBreakdownFilter } from 'scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { EditorFilterProps } from '~/types' + export function Breakdown({ insightProps }: EditorFilterProps): JSX.Element { const { breakdown, display, isTrends } = useValues(insightVizDataLogic(insightProps)) const { updateBreakdown, updateDisplay } = useActions(insightVizDataLogic(insightProps)) diff --git a/frontend/src/queries/nodes/InsightViz/ComputationTimeWithRefresh.tsx b/frontend/src/queries/nodes/InsightViz/ComputationTimeWithRefresh.tsx index 65c8dca84247e..be5b3044e2297 100644 --- a/frontend/src/queries/nodes/InsightViz/ComputationTimeWithRefresh.tsx +++ b/frontend/src/queries/nodes/InsightViz/ComputationTimeWithRefresh.tsx @@ -1,12 +1,11 @@ +import { Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' - import { dayjs } from 'lib/dayjs' import { usePeriodicRerender } from 'lib/hooks/usePeriodicRerender' - -import { insightLogic } from 'scenes/insights/insightLogic' import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' + import { dataNodeLogic } from '../DataNode/dataNodeLogic' -import { Link } from '@posthog/lemon-ui' export function ComputationTimeWithRefresh({ disableRefresh }: { disableRefresh?: boolean }): JSX.Element | null { const { lastRefresh, response } = useValues(dataNodeLogic) diff --git a/frontend/src/queries/nodes/InsightViz/EditorFilterGroup.tsx b/frontend/src/queries/nodes/InsightViz/EditorFilterGroup.tsx index 2dd91a9794077..039325e488621 100644 --- a/frontend/src/queries/nodes/InsightViz/EditorFilterGroup.tsx +++ b/frontend/src/queries/nodes/InsightViz/EditorFilterGroup.tsx @@ -1,13 +1,14 @@ -import { Fragment, useState } from 'react' -import type { InsightLogicProps, InsightModel, InsightEditorFilterGroup } from '~/types' -import { LemonButton } from 'lib/lemon-ui/LemonButton' +import './EditorFilterGroup.scss' + +import { PureField } from 'lib/forms/Field' import { IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' -import { slugify } from 'lib/utils' import { LemonBadge } from 'lib/lemon-ui/LemonBadge/LemonBadge' -import { PureField } from 'lib/forms/Field' -import { InsightQueryNode } from '~/queries/schema' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { slugify } from 'lib/utils' +import { Fragment, useState } from 'react' -import './EditorFilterGroup.scss' +import { InsightQueryNode } from '~/queries/schema' +import type { InsightEditorFilterGroup, InsightLogicProps, InsightModel } from '~/types' export interface EditorFilterGroupProps { editorFilterGroup: InsightEditorFilterGroup diff --git a/frontend/src/queries/nodes/InsightViz/EditorFilters.tsx b/frontend/src/queries/nodes/InsightViz/EditorFilters.tsx index fce497328f8ef..6c82cf18612f6 100644 --- a/frontend/src/queries/nodes/InsightViz/EditorFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/EditorFilters.tsx @@ -1,44 +1,43 @@ -import { CSSTransition } from 'react-transition-group' +import './EditorFilters.scss' + +import { LemonBanner, Link } from '@posthog/lemon-ui' import clsx from 'clsx' import { useValues } from 'kea' +import { NON_BREAKDOWN_DISPLAY_TYPES } from 'lib/constants' +import { CSSTransition } from 'react-transition-group' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { Attribution } from 'scenes/insights/EditorFilters/AttributionFilter' +import { FunnelsAdvanced } from 'scenes/insights/EditorFilters/FunnelsAdvanced' +import { FunnelsQuerySteps } from 'scenes/insights/EditorFilters/FunnelsQuerySteps' +import { PathsAdvanced } from 'scenes/insights/EditorFilters/PathsAdvanced' +import { PathsEventsTypes } from 'scenes/insights/EditorFilters/PathsEventTypes' +import { PathsExclusions } from 'scenes/insights/EditorFilters/PathsExclusions' +import { PathsHogQL } from 'scenes/insights/EditorFilters/PathsHogQL' +import { PathsTargetEnd, PathsTargetStart } from 'scenes/insights/EditorFilters/PathsTarget' +import { PathsWildcardGroups } from 'scenes/insights/EditorFilters/PathsWildcardGroups' +import { RetentionSummary } from 'scenes/insights/EditorFilters/RetentionSummary' +import { SamplingFilter } from 'scenes/insights/EditorFilters/SamplingFilter' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { userLogic } from 'scenes/userLogic' +import { InsightQueryNode } from '~/queries/schema' import { - InsightEditorFilterGroup, - InsightEditorFilter, - EditorFilterProps, - ChartDisplayType, AvailableFeature, + ChartDisplayType, + EditorFilterProps, + InsightEditorFilter, + InsightEditorFilterGroup, PathType, } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { userLogic } from 'scenes/userLogic' -import { NON_BREAKDOWN_DISPLAY_TYPES } from 'lib/constants' -import { InsightQueryNode } from '~/queries/schema' +import { Breakdown } from './Breakdown' import { EditorFilterGroup } from './EditorFilterGroup' -import { LifecycleToggles } from './LifecycleToggles' import { GlobalAndOrFilters } from './GlobalAndOrFilters' +import { LifecycleToggles } from './LifecycleToggles' +import { TrendsFormula } from './TrendsFormula' import { TrendsSeries } from './TrendsSeries' import { TrendsSeriesLabel } from './TrendsSeriesLabel' -import { TrendsFormula } from './TrendsFormula' -import { Breakdown } from './Breakdown' -import { PathsEventsTypes } from 'scenes/insights/EditorFilters/PathsEventTypes' -import { PathsTargetEnd, PathsTargetStart } from 'scenes/insights/EditorFilters/PathsTarget' -import { PathsExclusions } from 'scenes/insights/EditorFilters/PathsExclusions' -import { PathsWildcardGroups } from 'scenes/insights/EditorFilters/PathsWildcardGroups' -import { PathsAdvanced } from 'scenes/insights/EditorFilters/PathsAdvanced' -import { FunnelsQuerySteps } from 'scenes/insights/EditorFilters/FunnelsQuerySteps' -import { Attribution } from 'scenes/insights/EditorFilters/AttributionFilter' -import { FunnelsAdvanced } from 'scenes/insights/EditorFilters/FunnelsAdvanced' -import { RetentionSummary } from 'scenes/insights/EditorFilters/RetentionSummary' -import { SamplingFilter } from 'scenes/insights/EditorFilters/SamplingFilter' - -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' - -import './EditorFilters.scss' -import { PathsHogQL } from 'scenes/insights/EditorFilters/PathsHogQL' -import { LemonBanner, Link } from '@posthog/lemon-ui' export interface EditorFiltersProps { query: InsightQueryNode diff --git a/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx b/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx index 4ac27f78933e1..97de109870bc6 100644 --- a/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx @@ -1,14 +1,16 @@ -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { PropertyGroupFilters } from './PropertyGroupFilters/PropertyGroupFilters' import { useActions, useValues } from 'kea' -import { groupsModel } from '~/models/groupsModel' -import { actionsModel } from '~/models/actionsModel' -import { getAllEventNames } from './utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { EditorFilterProps } from '~/types' -import { StickinessQuery, TrendsQuery } from '~/queries/schema' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { actionsModel } from '~/models/actionsModel' +import { groupsModel } from '~/models/groupsModel' +import { StickinessQuery, TrendsQuery } from '~/queries/schema' +import { EditorFilterProps } from '~/types' + +import { PropertyGroupFilters } from './PropertyGroupFilters/PropertyGroupFilters' +import { getAllEventNames } from './utils' + export function GlobalAndOrFilters({ insightProps }: EditorFilterProps): JSX.Element { const { actions: allActions } = useValues(actionsModel) const { groupsTaxonomicTypes } = useValues(groupsModel) diff --git a/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx b/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx index 37772a3c233e5..34490c40427a9 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx +++ b/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx @@ -1,31 +1,30 @@ -import { ReactNode } from 'react' +import { LemonButton } from '@posthog/lemon-ui' import { useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' - -import { InsightDateFilter } from 'scenes/insights/filters/InsightDateFilter' +import { ChartFilter } from 'lib/components/ChartFilter' +import { CompareFilter } from 'lib/components/CompareFilter/CompareFilter' import { IntervalFilter } from 'lib/components/IntervalFilter' import { SmoothingFilter } from 'lib/components/SmoothingFilter/SmoothingFilter' -import { RetentionDatePicker } from 'scenes/insights/RetentionDatePicker' -import { RetentionReferencePicker } from 'scenes/insights/filters/RetentionReferencePicker' -import { PathStepPicker } from 'scenes/insights/views/Paths/PathStepPicker' -import { CompareFilter } from 'lib/components/CompareFilter/CompareFilter' import { UnitPicker } from 'lib/components/UnitPicker/UnitPicker' -import { ChartFilter } from 'lib/components/ChartFilter' -import { FunnelDisplayLayoutPicker } from 'scenes/insights/views/Funnels/FunnelDisplayLayoutPicker' -import { FunnelBinsPicker } from 'scenes/insights/views/Funnels/FunnelBinsPicker' -import { ValueOnSeriesFilter } from 'scenes/insights/EditorFilters/ValueOnSeriesFilter' -import { PercentStackViewFilter } from 'scenes/insights/EditorFilters/PercentStackViewFilter' -import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' +import { FEATURE_FLAGS, NON_TIME_SERIES_DISPLAY_TYPES } from 'lib/constants' import { LemonMenu, LemonMenuItems } from 'lib/lemon-ui/LemonMenu' -import { LemonButton } from '@posthog/lemon-ui' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { ReactNode } from 'react' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' import { axisLabel } from 'scenes/insights/aggregationAxisFormat' -import { ChartDisplayType } from '~/types' +import { PercentStackViewFilter } from 'scenes/insights/EditorFilters/PercentStackViewFilter' import { ShowLegendFilter } from 'scenes/insights/EditorFilters/ShowLegendFilter' -import { FEATURE_FLAGS, NON_TIME_SERIES_DISPLAY_TYPES } from 'lib/constants' +import { ValueOnSeriesFilter } from 'scenes/insights/EditorFilters/ValueOnSeriesFilter' +import { InsightDateFilter } from 'scenes/insights/filters/InsightDateFilter' +import { RetentionReferencePicker } from 'scenes/insights/filters/RetentionReferencePicker' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { RetentionDatePicker } from 'scenes/insights/RetentionDatePicker' +import { FunnelBinsPicker } from 'scenes/insights/views/Funnels/FunnelBinsPicker' +import { FunnelDisplayLayoutPicker } from 'scenes/insights/views/Funnels/FunnelDisplayLayoutPicker' +import { PathStepPicker } from 'scenes/insights/views/Paths/PathStepPicker' +import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' + +import { ChartDisplayType } from '~/types' export function InsightDisplayConfig(): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/queries/nodes/InsightViz/InsightResultMetadata.tsx b/frontend/src/queries/nodes/InsightViz/InsightResultMetadata.tsx index 66c84ac6f1a46..93c353f21704e 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightResultMetadata.tsx +++ b/frontend/src/queries/nodes/InsightViz/InsightResultMetadata.tsx @@ -1,5 +1,4 @@ import { useValues } from 'kea' - import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' diff --git a/frontend/src/queries/nodes/InsightViz/InsightViz.scss b/frontend/src/queries/nodes/InsightViz/InsightViz.scss index a6197196fe264..01315b0d6877b 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightViz.scss +++ b/frontend/src/queries/nodes/InsightViz/InsightViz.scss @@ -68,6 +68,12 @@ } } +.WebAnalyticsDashboard { + .InsightVizDisplay { + --insight-viz-min-height: 25rem; + } +} + .RetentionContainer { width: 100%; display: flex; diff --git a/frontend/src/queries/nodes/InsightViz/InsightViz.tsx b/frontend/src/queries/nodes/InsightViz/InsightViz.tsx index 067002682f68f..10d8e1c745f08 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightViz.tsx +++ b/frontend/src/queries/nodes/InsightViz/InsightViz.tsx @@ -1,24 +1,23 @@ -import { BindLogic, useValues } from 'kea' -import clsx from 'clsx' +import './InsightViz.scss' +import clsx from 'clsx' +import { BindLogic, useValues } from 'kea' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { useState } from 'react' import { insightLogic } from 'scenes/insights/insightLogic' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { isFunnelsQuery } from '~/queries/utils' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { dataNodeLogic, DataNodeLogicProps } from '../DataNode/dataNodeLogic' import { InsightVizNode } from '~/queries/schema' import { QueryContext } from '~/queries/types' +import { isFunnelsQuery } from '~/queries/utils' +import { InsightLogicProps, ItemMode } from '~/types' -import { InsightVizDisplay } from './InsightVizDisplay' +import { dataNodeLogic, DataNodeLogicProps } from '../DataNode/dataNodeLogic' import { EditorFilters } from './EditorFilters' -import { InsightLogicProps, ItemMode } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { InsightVizDisplay } from './InsightVizDisplay' import { getCachedResults } from './utils' -import { useState } from 'react' - -import './InsightViz.scss' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' /** The key for the dataNodeLogic mounted by an InsightViz for insight of insightProps */ export const insightVizDataNodeKey = (insightProps: InsightLogicProps): string => { diff --git a/frontend/src/queries/nodes/InsightViz/InsightVizDisplay.tsx b/frontend/src/queries/nodes/InsightViz/InsightVizDisplay.tsx index 5c726071e38a5..e948254cfc16d 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightVizDisplay.tsx +++ b/frontend/src/queries/nodes/InsightViz/InsightVizDisplay.tsx @@ -1,23 +1,13 @@ +import clsx from 'clsx' import { useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { insightNavLogic } from 'scenes/insights/InsightNav/insightNavLogic' - -import { QueryContext } from '~/queries/types' -import { ChartDisplayType, ExporterFormat, FunnelVizType, InsightType, ItemMode } from '~/types' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Animation } from 'lib/components/Animation/Animation' import { AnimationType } from 'lib/animations/animations' +import { Animation } from 'lib/components/Animation/Animation' import { ExportButton } from 'lib/components/ExportButton/ExportButton' - -import { InsightDisplayConfig } from './InsightDisplayConfig' +import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { Funnel } from 'scenes/funnels/Funnel' import { FunnelCanvasLabel } from 'scenes/funnels/FunnelCanvasLabel' -import { TrendInsight } from 'scenes/trends/Trends' -import { RetentionContainer } from 'scenes/retention/RetentionContainer' -import { Paths } from 'scenes/paths/Paths' -import { InsightsTable } from 'scenes/insights/views/InsightsTable/InsightsTable' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' import { FunnelInvalidExclusionState, FunnelSingleStepState, @@ -25,14 +15,23 @@ import { InsightErrorState, InsightTimeoutState, } from 'scenes/insights/EmptyStates' -import { PathCanvasLabel } from 'scenes/paths/PathsLabel' -import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend' -import { FunnelStepsTable } from 'scenes/insights/views/Funnels/FunnelStepsTable' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightNavLogic } from 'scenes/insights/InsightNav/insightNavLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { FunnelCorrelation } from 'scenes/insights/views/Funnels/FunnelCorrelation' +import { FunnelStepsTable } from 'scenes/insights/views/Funnels/FunnelStepsTable' +import { InsightsTable } from 'scenes/insights/views/InsightsTable/InsightsTable' +import { Paths } from 'scenes/paths/Paths' +import { PathCanvasLabel } from 'scenes/paths/PathsLabel' +import { RetentionContainer } from 'scenes/retention/RetentionContainer' +import { TrendInsight } from 'scenes/trends/Trends' + +import { QueryContext } from '~/queries/types' +import { ChartDisplayType, ExporterFormat, FunnelVizType, InsightType, ItemMode } from '~/types' + +import { InsightDisplayConfig } from './InsightDisplayConfig' import { InsightResultMetadata } from './InsightResultMetadata' -import clsx from 'clsx' -import { Funnel } from 'scenes/funnels/Funnel' export function InsightVizDisplay({ disableHeader, diff --git a/frontend/src/queries/nodes/InsightViz/LifecycleToggles.tsx b/frontend/src/queries/nodes/InsightViz/LifecycleToggles.tsx index 311f9563cd33b..04ff9cd56e709 100644 --- a/frontend/src/queries/nodes/InsightViz/LifecycleToggles.tsx +++ b/frontend/src/queries/nodes/InsightViz/LifecycleToggles.tsx @@ -1,9 +1,10 @@ -import { LifecycleFilter } from '~/queries/schema' -import { EditorFilterProps, LifecycleToggle } from '~/types' import { LemonCheckbox, LemonLabel } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { LifecycleFilter } from '~/queries/schema' +import { EditorFilterProps, LifecycleToggle } from '~/types' + const lifecycles: { name: LifecycleToggle; tooltip: string; color: string }[] = [ { name: 'new', diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.stories.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.stories.tsx index 69799198d8bdc..a1ddc6d8a9942 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.stories.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.stories.tsx @@ -1,5 +1,6 @@ -import { useState } from 'react' import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { useState } from 'react' + import { FilterLogicalOperator } from '~/types' import { AndOrFilterSelect } from './AndOrFilterSelect' diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx index 2e9407f39ac25..b6583793f1523 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/AndOrFilterSelect.tsx @@ -1,4 +1,5 @@ import { LemonSelect } from '@posthog/lemon-ui' + import { FilterLogicalOperator } from '~/types' interface AndOrFilterSelectProps { diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.scss b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.scss index 0c2b080492e42..6637f4f265e04 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.scss +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.scss @@ -1,6 +1,11 @@ .PropertyGroupFilters { .property-group { background-color: var(--side); + + .posthog-3000 & { + border-width: 1px; + } + padding: 0.5rem; border-radius: 4px; } diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx index 85ddfae03059c..f1be02c46d0b0 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx @@ -1,18 +1,21 @@ -import { useValues, BindLogic, useActions } from 'kea' -import { PropertyGroupFilterValue, AnyPropertyFilter, InsightLogicProps } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import './PropertyGroupFilters.scss' -import { propertyGroupFilterLogic } from './propertyGroupFilterLogic' + +import { LemonButton, LemonDivider } from '@posthog/lemon-ui' +import { BindLogic, useActions, useValues } from 'kea' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' import { isPropertyGroupFilterLike } from 'lib/components/PropertyFilters/utils' -import { GlobalFiltersTitle } from 'scenes/insights/common' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { IconCopy, IconDelete, IconPlusMini } from 'lib/lemon-ui/icons' -import { TestAccountFilter } from '../filters/TestAccountFilter' -import { LemonButton, LemonDivider } from '@posthog/lemon-ui' import React from 'react' +import { GlobalFiltersTitle } from 'scenes/insights/common' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + import { InsightQueryNode, StickinessQuery, TrendsQuery } from '~/queries/schema' +import { AnyPropertyFilter, InsightLogicProps, PropertyGroupFilterValue } from '~/types' + +import { TestAccountFilter } from '../filters/TestAccountFilter' import { AndOrFilterSelect } from './AndOrFilterSelect' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { propertyGroupFilterLogic } from './propertyGroupFilterLogic' type PropertyGroupFiltersProps = { insightProps: InsightLogicProps diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/propertyGroupFilterLogic.ts b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/propertyGroupFilterLogic.ts index aa99e6a2b2a22..3b20beb8139a7 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/propertyGroupFilterLogic.ts +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/propertyGroupFilterLogic.ts @@ -1,12 +1,12 @@ import { actions, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' - -import { PropertyGroupFilter, FilterLogicalOperator, EmptyPropertyFilter } from '~/types' - -import type { propertyGroupFilterLogicType } from './propertyGroupFilterLogicType' +import { convertPropertiesToPropertyGroup } from 'lib/components/PropertyFilters/utils' import { objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + import { StickinessQuery, TrendsQuery } from '~/queries/schema' -import { convertPropertiesToPropertyGroup } from 'lib/components/PropertyFilters/utils' +import { EmptyPropertyFilter, FilterLogicalOperator, PropertyGroupFilter } from '~/types' + +import type { propertyGroupFilterLogicType } from './propertyGroupFilterLogicType' export type PropertyGroupFilterLogicProps = { pageKey: string diff --git a/frontend/src/queries/nodes/InsightViz/TrendsFormula.tsx b/frontend/src/queries/nodes/InsightViz/TrendsFormula.tsx index 8e8f565e62e00..a3003a10a7e19 100644 --- a/frontend/src/queries/nodes/InsightViz/TrendsFormula.tsx +++ b/frontend/src/queries/nodes/InsightViz/TrendsFormula.tsx @@ -1,9 +1,10 @@ -import { useEffect, useState } from 'react' -import { EditorFilterProps } from '~/types' -import { useActions, useValues } from 'kea' import { LemonInput } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { useEffect, useState } from 'react' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { EditorFilterProps } from '~/types' + // When updating this regex, remember to update the regex with the same name in mixins/common.py const ALLOWED_FORMULA_CHARACTERS = /^[a-zA-Z \-*^0-9+/().]+$/ diff --git a/frontend/src/queries/nodes/InsightViz/TrendsSeries.tsx b/frontend/src/queries/nodes/InsightViz/TrendsSeries.tsx index b726171a6e43d..fa0f8c49d2aef 100644 --- a/frontend/src/queries/nodes/InsightViz/TrendsSeries.tsx +++ b/frontend/src/queries/nodes/InsightViz/TrendsSeries.tsx @@ -1,20 +1,21 @@ -import { useValues, useActions } from 'kea' -import { groupsModel } from '~/models/groupsModel' -import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' -import { FilterType } from '~/types' -import { alphabet } from 'lib/utils' -import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' +import { useActions, useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { SINGLE_SERIES_DISPLAY_TYPES } from 'lib/constants' -import { TrendsQuery, FunnelsQuery, LifecycleQuery, StickinessQuery } from '~/queries/schema' -import { isInsightQueryNode } from '~/queries/utils' -import { queryNodeToFilter } from '../InsightQuery/utils/queryNodeToFilter' -import { actionsAndEventsToSeries } from '../InsightQuery/utils/filtersToQueryNode' - -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { alphabet } from 'lib/utils' +import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' +import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { groupsModel } from '~/models/groupsModel' +import { FunnelsQuery, LifecycleQuery, StickinessQuery, TrendsQuery } from '~/queries/schema' +import { isInsightQueryNode } from '~/queries/utils' +import { FilterType } from '~/types' + +import { actionsAndEventsToSeries } from '../InsightQuery/utils/filtersToQueryNode' +import { queryNodeToFilter } from '../InsightQuery/utils/queryNodeToFilter' + export function TrendsSeries(): JSX.Element | null { const { insightProps } = useValues(insightLogic) const { querySource, isTrends, isLifecycle, isStickiness, display, hasFormula } = useValues( diff --git a/frontend/src/queries/nodes/InsightViz/TrendsSeriesLabel.tsx b/frontend/src/queries/nodes/InsightViz/TrendsSeriesLabel.tsx index 9e1e8e183ba68..b524f539f0b93 100644 --- a/frontend/src/queries/nodes/InsightViz/TrendsSeriesLabel.tsx +++ b/frontend/src/queries/nodes/InsightViz/TrendsSeriesLabel.tsx @@ -1,11 +1,12 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { EditorFilterProps } from '~/types' import { SINGLE_SERIES_DISPLAY_TYPES } from 'lib/constants' -import { LemonButton } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { IconCalculate } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { EditorFilterProps } from '~/types' + export function TrendsSeriesLabel({ insightProps }: EditorFilterProps): JSX.Element { const { hasFormula, isTrends, display, series } = useValues(insightVizDataLogic(insightProps)) const { updateInsightFilter } = useActions(insightVizDataLogic(insightProps)) diff --git a/frontend/src/queries/nodes/InsightViz/filters/TestAccountFilter.tsx b/frontend/src/queries/nodes/InsightViz/filters/TestAccountFilter.tsx index 1aae07d9f5860..186b4e7eeba3f 100644 --- a/frontend/src/queries/nodes/InsightViz/filters/TestAccountFilter.tsx +++ b/frontend/src/queries/nodes/InsightViz/filters/TestAccountFilter.tsx @@ -1,12 +1,12 @@ -import { useActions, useValues } from 'kea' - -import { teamLogic } from 'scenes/teamLogic' import { LemonButton, LemonSwitch } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { IconSettings } from 'lib/lemon-ui/icons' -import { InsightQueryNode } from '~/queries/schema' import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' +import { InsightQueryNode } from '~/queries/schema' + type TestAccountFilterProps = { query: InsightQueryNode setQuery: (query: InsightQueryNode) => void diff --git a/frontend/src/queries/nodes/InsightViz/utils.ts b/frontend/src/queries/nodes/InsightViz/utils.ts index b21948c769209..b06ed5b228248 100644 --- a/frontend/src/queries/nodes/InsightViz/utils.ts +++ b/frontend/src/queries/nodes/InsightViz/utils.ts @@ -1,7 +1,7 @@ -import { ActionsNode, BreakdownFilter, EventsNode, InsightQueryNode, TrendsQuery } from '~/queries/schema' -import { ActionType, ChartDisplayType, InsightModel, IntervalType } from '~/types' -import { seriesToActionsAndEvents } from '../InsightQuery/utils/queryNodeToFilter' +import equal from 'fast-deep-equal' import { getEventNamesForAction, isEmptyObject } from 'lib/utils' + +import { ActionsNode, BreakdownFilter, EventsNode, InsightQueryNode, TrendsQuery } from '~/queries/schema' import { isInsightQueryWithBreakdown, isInsightQueryWithSeries, @@ -9,8 +9,10 @@ import { isStickinessQuery, isTrendsQuery, } from '~/queries/utils' +import { ActionType, ChartDisplayType, InsightModel, IntervalType } from '~/types' + import { filtersToQueryNode } from '../InsightQuery/utils/filtersToQueryNode' -import equal from 'fast-deep-equal' +import { seriesToActionsAndEvents } from '../InsightQuery/utils/queryNodeToFilter' export const getAllEventNames = (query: InsightQueryNode, allActions: ActionType[]): string[] => { const { actions, events } = seriesToActionsAndEvents((query as TrendsQuery).series || []) @@ -103,6 +105,14 @@ export const getShowValueOnSeries = (query: InsightQueryNode): boolean | undefin } } +export const getShowLabelsOnSeries = (query: InsightQueryNode): boolean | undefined => { + if (isTrendsQuery(query)) { + return query.trendsFilter?.show_labels_on_series + } else { + return undefined + } +} + export const getShowPercentStackView = (query: InsightQueryNode): boolean | undefined => { if (isTrendsQuery(query)) { return query.trendsFilter?.show_percent_stack_view diff --git a/frontend/src/queries/nodes/Node/EditHogQLButton.tsx b/frontend/src/queries/nodes/Node/EditHogQLButton.tsx index f3a901c00aba1..74e1e6c41fef6 100644 --- a/frontend/src/queries/nodes/Node/EditHogQLButton.tsx +++ b/frontend/src/queries/nodes/Node/EditHogQLButton.tsx @@ -1,7 +1,8 @@ +import { IconQueryEditor } from 'lib/lemon-ui/icons' import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import { NodeKind } from '~/queries/schema' import { urls } from 'scenes/urls' -import { IconQueryEditor } from 'lib/lemon-ui/icons' + +import { NodeKind } from '~/queries/schema' export interface EditHogQLButtonProps extends LemonButtonProps { hogql: string diff --git a/frontend/src/queries/nodes/Node/OpenEditorButton.tsx b/frontend/src/queries/nodes/Node/OpenEditorButton.tsx index dd409bbfa6d54..23d2b55543178 100644 --- a/frontend/src/queries/nodes/Node/OpenEditorButton.tsx +++ b/frontend/src/queries/nodes/Node/OpenEditorButton.tsx @@ -1,7 +1,8 @@ +import { IconPreview } from 'lib/lemon-ui/icons' import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import { Node } from '~/queries/schema' import { urls } from 'scenes/urls' -import { IconPreview } from 'lib/lemon-ui/icons' + +import { Node } from '~/queries/schema' export interface OpenEditorButtonProps extends LemonButtonProps { query: Node | null diff --git a/frontend/src/queries/nodes/PersonsNode/DeletePersonButton.tsx b/frontend/src/queries/nodes/PersonsNode/DeletePersonButton.tsx index b7523213593ec..4565a6cd5b22a 100644 --- a/frontend/src/queries/nodes/PersonsNode/DeletePersonButton.tsx +++ b/frontend/src/queries/nodes/PersonsNode/DeletePersonButton.tsx @@ -1,9 +1,10 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconDelete } from 'lib/lemon-ui/icons' import { useActions } from 'kea' -import { PersonType } from '~/types' +import { IconDelete } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { personDeleteModalLogic } from 'scenes/persons/personDeleteModalLogic' + import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' +import { PersonType } from '~/types' interface DeletePersonButtonProps { person: PersonType diff --git a/frontend/src/queries/nodes/PersonsNode/PersonPropertyFilters.tsx b/frontend/src/queries/nodes/PersonsNode/PersonPropertyFilters.tsx index f8ddaa48b44b1..43f633da0d80a 100644 --- a/frontend/src/queries/nodes/PersonsNode/PersonPropertyFilters.tsx +++ b/frontend/src/queries/nodes/PersonsNode/PersonPropertyFilters.tsx @@ -1,9 +1,10 @@ -import { PersonsNode, PersonsQuery } from '~/queries/schema' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { PersonPropertyFilter } from '~/types' -import { useState } from 'react' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { useState } from 'react' + +import { PersonsNode, PersonsQuery } from '~/queries/schema' import { isPersonsQuery } from '~/queries/utils' +import { PersonPropertyFilter } from '~/types' interface PersonPropertyFiltersProps { query: PersonsNode | PersonsQuery diff --git a/frontend/src/queries/nodes/PersonsNode/PersonsSearch.tsx b/frontend/src/queries/nodes/PersonsNode/PersonsSearch.tsx index a190d1b4434b6..b660556ea23a8 100644 --- a/frontend/src/queries/nodes/PersonsNode/PersonsSearch.tsx +++ b/frontend/src/queries/nodes/PersonsNode/PersonsSearch.tsx @@ -1,8 +1,9 @@ -import { PersonsNode, PersonsQuery } from '~/queries/schema' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { IconInfo } from 'lib/lemon-ui/icons' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { Tooltip } from 'lib/lemon-ui/Tooltip' + import { useDebouncedQuery } from '~/queries/hooks/useDebouncedQuery' +import { PersonsNode, PersonsQuery } from '~/queries/schema' interface PersonSearchProps { query: PersonsNode | PersonsQuery diff --git a/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx b/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx index 11b4e87e03b1e..5ab40c629a87e 100644 --- a/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx +++ b/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx @@ -1,14 +1,13 @@ import { useValues } from 'kea' - +import { AnimationType } from 'lib/animations/animations' +import { Animation } from 'lib/components/Animation/Animation' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' import { insightLogic } from 'scenes/insights/insightLogic' + import { Query } from '~/queries/Query/Query' import { SavedInsightNode } from '~/queries/schema' import { QueryContext } from '~/queries/types' - import { InsightLogicProps } from '~/types' -import { Animation } from 'lib/components/Animation/Animation' -import { AnimationType } from 'lib/animations/animations' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' interface InsightProps { query: SavedInsightNode diff --git a/frontend/src/queries/nodes/TimeToSeeData/TimeToSeeData.tsx b/frontend/src/queries/nodes/TimeToSeeData/TimeToSeeData.tsx index 510078a1c1e80..f4ab309ec11cf 100644 --- a/frontend/src/queries/nodes/TimeToSeeData/TimeToSeeData.tsx +++ b/frontend/src/queries/nodes/TimeToSeeData/TimeToSeeData.tsx @@ -1,12 +1,14 @@ +import { useValues } from 'kea' +import { CodeEditor } from 'lib/components/CodeEditors' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { useState } from 'react' import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' + import { AnyResponseType, NodeKind, TimeToSeeDataNode } from '~/queries/schema' -import { useValues } from 'kea' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' + import { dataNodeLogic } from '../DataNode/dataNodeLogic' import { Trace } from './Trace/Trace' import { TimeToSeeSessionNode } from './types' -import { CodeEditor } from 'lib/components/CodeEditors' let uniqueNode = 0 diff --git a/frontend/src/queries/nodes/TimeToSeeData/Trace/Trace.tsx b/frontend/src/queries/nodes/TimeToSeeData/Trace/Trace.tsx index bcd5f3e4acc3d..64293e6b71621 100644 --- a/frontend/src/queries/nodes/TimeToSeeData/Trace/Trace.tsx +++ b/frontend/src/queries/nodes/TimeToSeeData/Trace/Trace.tsx @@ -1,15 +1,16 @@ +import { Tooltip } from '@posthog/lemon-ui' import clsx from 'clsx' import { BindLogic, useValues } from 'kea' import { getSeriesColor } from 'lib/colors' import { TZLabel } from 'lib/components/TZLabel' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { IconSad } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { humanFriendlyDuration, humanFriendlyMilliseconds } from 'lib/utils' import { RefCallback, useEffect, useState } from 'react' import useResizeObserver from 'use-resize-observer' + import { isInteractionNode, isQueryNode, isSessionNode, TimeToSeeNode, TimeToSeeSessionNode } from '../types' import { sessionNodeFacts, SpanData, traceLogic } from './traceLogic' -import { Tooltip } from '@posthog/lemon-ui' export interface TraceProps { timeToSeeSession: TimeToSeeSessionNode diff --git a/frontend/src/queries/nodes/TimeToSeeData/Trace/traceLogic.tsx b/frontend/src/queries/nodes/TimeToSeeData/Trace/traceLogic.tsx index 37f2654050acf..c36416b2fe033 100644 --- a/frontend/src/queries/nodes/TimeToSeeData/Trace/traceLogic.tsx +++ b/frontend/src/queries/nodes/TimeToSeeData/Trace/traceLogic.tsx @@ -2,6 +2,7 @@ import { kea, key, path, props, selectors } from 'kea' import { dayjs } from 'lib/dayjs' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { humanFriendlyMilliseconds } from 'lib/utils' + import { isSessionNode, TimeToSeeInteractionNode, @@ -9,7 +10,6 @@ import { TimeToSeeQueryNode, TimeToSeeSessionNode, } from '../types' - import type { traceLogicType } from './traceLogicType' export interface TraceLogicProps { diff --git a/frontend/src/queries/nodes/WebOverview/EvenlyDistributedRows.tsx b/frontend/src/queries/nodes/WebOverview/EvenlyDistributedRows.tsx index d40781079d9e4..eb9d32084d6c0 100644 --- a/frontend/src/queries/nodes/WebOverview/EvenlyDistributedRows.tsx +++ b/frontend/src/queries/nodes/WebOverview/EvenlyDistributedRows.tsx @@ -1,5 +1,5 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react' import clsx from 'clsx' +import React, { useCallback, useEffect, useRef, useState } from 'react' export const EvenlyDistributedRows = ({ children, diff --git a/frontend/src/queries/nodes/WebOverview/WebOverview.tsx b/frontend/src/queries/nodes/WebOverview/WebOverview.tsx index 9de6ccdbac0b3..5612f1060eda8 100644 --- a/frontend/src/queries/nodes/WebOverview/WebOverview.tsx +++ b/frontend/src/queries/nodes/WebOverview/WebOverview.tsx @@ -1,13 +1,15 @@ -import { useState } from 'react' -import { AnyResponseType, WebOverviewItem, WebOverviewQuery, WebOverviewQueryResponse } from '~/queries/schema' import { useValues } from 'kea' +import { getColorVar } from 'lib/colors' +import { IconTrendingDown, IconTrendingFlat, IconTrendingUp } from 'lib/lemon-ui/icons' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { dataNodeLogic } from '../DataNode/dataNodeLogic' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { humanFriendlyDuration, humanFriendlyLargeNumber, isNotNil } from 'lib/utils' -import { IconTrendingDown, IconTrendingFlat, IconTrendingUp } from 'lib/lemon-ui/icons' -import { getColorVar } from 'lib/colors' +import { useState } from 'react' + import { EvenlyDistributedRows } from '~/queries/nodes/WebOverview/EvenlyDistributedRows' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { AnyResponseType, WebOverviewItem, WebOverviewQuery, WebOverviewQueryResponse } from '~/queries/schema' + +import { dataNodeLogic } from '../DataNode/dataNodeLogic' let uniqueNode = 0 export function WebOverview(props: { query: WebOverviewQuery; cachedResults?: AnyResponseType }): JSX.Element | null { diff --git a/frontend/src/queries/query.test.ts b/frontend/src/queries/query.test.ts index 5ae28a1a657e0..7393f59d2868c 100644 --- a/frontend/src/queries/query.test.ts +++ b/frontend/src/queries/query.test.ts @@ -1,9 +1,10 @@ +import posthog from 'posthog-js' + +import { useMocks } from '~/mocks/jest' import { query, queryExportContext } from '~/queries/query' import { EventsQuery, HogQLQuery, NodeKind } from '~/queries/schema' -import { PropertyFilterType, PropertyOperator } from '~/types' import { initKeaTests } from '~/test/init' -import posthog from 'posthog-js' -import { useMocks } from '~/mocks/jest' +import { PropertyFilterType, PropertyOperator } from '~/types' describe('query', () => { beforeEach(() => { diff --git a/frontend/src/queries/query.ts b/frontend/src/queries/query.ts index 01c218a290e6c..29d08863a62c0 100644 --- a/frontend/src/queries/query.ts +++ b/frontend/src/queries/query.ts @@ -1,22 +1,11 @@ -import posthog from 'posthog-js' -import { DataNode, HogQLQuery, HogQLQueryResponse, NodeKind, PersonsNode } from './schema' -import { - isInsightQueryNode, - isEventsQuery, - isPersonsNode, - isTimeToSeeDataSessionsQuery, - isTimeToSeeDataQuery, - isDataTableNode, - isTimeToSeeDataSessionsNode, - isHogQLQuery, - isInsightVizNode, - isQueryWithHogQLSupport, - isPersonsQuery, - isLifecycleQuery, -} from './utils' import api, { ApiMethodOptions } from 'lib/api' +import { FEATURE_FLAGS } from 'lib/constants' +import { now } from 'lib/dayjs' +import { currentSessionId } from 'lib/internalMetrics' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { delay, flattenObject, toParams } from 'lib/utils' import { getCurrentTeamId } from 'lib/utils/logics' -import { AnyPartialFilterType, OnlineExportContext, QueryExportContext } from '~/types' +import posthog from 'posthog-js' import { filterTrendsClientSideParams, isFunnelsFilter, @@ -26,12 +15,25 @@ import { isStickinessFilter, isTrendsFilter, } from 'scenes/insights/sharedUtils' -import { flattenObject, delay, toParams } from 'lib/utils' + +import { AnyPartialFilterType, OnlineExportContext, QueryExportContext } from '~/types' + import { queryNodeToFilter } from './nodes/InsightQuery/utils/queryNodeToFilter' -import { now } from 'lib/dayjs' -import { currentSessionId } from 'lib/internalMetrics' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' +import { DataNode, HogQLQuery, HogQLQueryResponse, NodeKind, PersonsNode } from './schema' +import { + isDataTableNode, + isEventsQuery, + isHogQLQuery, + isInsightQueryNode, + isInsightVizNode, + isLifecycleQuery, + isPersonsNode, + isPersonsQuery, + isQueryWithHogQLSupport, + isTimeToSeeDataQuery, + isTimeToSeeDataSessionsNode, + isTimeToSeeDataSessionsQuery, +} from './utils' const QUERY_ASYNC_MAX_INTERVAL_SECONDS = 10 const QUERY_ASYNC_TOTAL_POLL_SECONDS = 300 diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index d7b3b2ca0da1d..7d5f3cb358ffc 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -3020,6 +3020,9 @@ }, "type": "array" }, + "show_labels_on_series": { + "type": "boolean" + }, "show_legend": { "type": "boolean" }, @@ -3140,6 +3143,18 @@ "additionalProperties": false, "description": "Chart specific rendering options. Use ChartRenderingMetadata for non-serializable values, e.g. onClick handlers", "properties": { + "ActionsPie": { + "additionalProperties": false, + "properties": { + "disableHoverOffset": { + "type": "boolean" + }, + "hideAggregation": { + "type": "boolean" + } + }, + "type": "object" + }, "RETENTION": { "additionalProperties": false, "properties": { diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 5e0f19452d438..1f035b9d1acba 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -4,6 +4,7 @@ import { Breakdown, BreakdownKeyType, BreakdownType, + ChartDisplayType, CountPerActorMathType, EventPropertyFilter, EventType, @@ -403,6 +404,10 @@ export interface VizSpecificOptions { hideSizeColumn?: boolean useSmallLayout?: boolean } + [ChartDisplayType.ActionsPie]?: { + disableHoverOffset?: boolean + hideAggregation?: boolean + } } export interface InsightVizNode extends Node, InsightVizNodeViewProps { @@ -424,7 +429,6 @@ interface InsightVizNodeViewProps { embedded?: boolean suppressSessionAnalysisWarning?: boolean hidePersonsModal?: boolean - vizSpecificOptions?: VizSpecificOptions } diff --git a/frontend/src/queries/types.ts b/frontend/src/queries/types.ts index f1e63d8f54549..2ae3a75e65009 100644 --- a/frontend/src/queries/types.ts +++ b/frontend/src/queries/types.ts @@ -1,6 +1,7 @@ -import { ChartDisplayType, InsightLogicProps, TrendResult } from '~/types' import { ComponentType, HTMLProps } from 'react' + import { DataTableNode } from '~/queries/schema' +import { ChartDisplayType, GraphPointPayload, InsightLogicProps, TrendResult } from '~/types' /** Pass custom metadata to queries. Used for e.g. custom columns in the DataTable. */ export interface QueryContext { @@ -24,6 +25,9 @@ export interface ChartRenderingMetadata { [ChartDisplayType.WorldMap]?: { countryProps?: (countryCode: string, countryData: TrendResult | undefined) => Omit, 'key'> } + [ChartDisplayType.ActionsPie]?: { + onSegmentClick?: (payload: GraphPointPayload) => void + } } export type QueryContextColumnTitleComponent = ComponentType<{ diff --git a/frontend/src/queries/utils.test.ts b/frontend/src/queries/utils.test.ts index d20ea8c377af2..60a561eb68b2c 100644 --- a/frontend/src/queries/utils.test.ts +++ b/frontend/src/queries/utils.test.ts @@ -1,10 +1,12 @@ +import { MOCK_TEAM_ID } from 'lib/api.mock' import { dayjs } from 'lib/dayjs' -import { hogql } from './utils' import { teamLogic } from 'scenes/teamLogic' + import { initKeaTests } from '~/test/init' -import { MOCK_TEAM_ID } from 'lib/api.mock' import { AppContext, TeamType } from '~/types' +import { hogql } from './utils' + window.POSTHOG_APP_CONTEXT = { current_team: { id: MOCK_TEAM_ID } } as unknown as AppContext describe('hogql tag', () => { diff --git a/frontend/src/queries/utils.ts b/frontend/src/queries/utils.ts index de393a7fbd659..79151b209a89e 100644 --- a/frontend/src/queries/utils.ts +++ b/frontend/src/queries/utils.ts @@ -1,3 +1,7 @@ +import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' +import { dayjs } from 'lib/dayjs' +import { teamLogic } from 'scenes/teamLogic' + import { ActionsNode, DatabaseSchemaQuery, @@ -32,9 +36,6 @@ import { WebStatsTableQuery, WebTopClicksQuery, } from '~/queries/schema' -import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' -import { dayjs } from 'lib/dayjs' -import { teamLogic } from 'scenes/teamLogic' export function isDataNode(node?: Node | null): node is EventsQuery | PersonsNode | TimeToSeeDataSessionsQuery { return ( diff --git a/frontend/src/scenes/App.tsx b/frontend/src/scenes/App.tsx index 225efc7dffeaa..1bc924fbc7bfc 100644 --- a/frontend/src/scenes/App.tsx +++ b/frontend/src/scenes/App.tsx @@ -1,29 +1,31 @@ -import { kea, useMountedLogic, useValues, BindLogic, path, connect, actions, reducers, selectors, events } from 'kea' -import { ToastContainer, Slide } from 'react-toastify' -import { preflightLogic } from './PreflightCheck/preflightLogic' -import { userLogic } from 'scenes/userLogic' -import { sceneLogic } from 'scenes/sceneLogic' +import { actions, BindLogic, connect, events, kea, path, reducers, selectors, useMountedLogic, useValues } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { ToastCloseButton } from 'lib/lemon-ui/lemonToast' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import type { appLogicType } from './AppType' -import { teamLogic } from './teamLogic' -import { LoadedScene } from 'scenes/sceneTypes' +import { inAppPromptLogic } from 'lib/logic/inAppPrompt/inAppPromptLogic' +import { useEffect } from 'react' +import { Slide, ToastContainer } from 'react-toastify' +import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' import { appScenes } from 'scenes/appScenes' -import { Navigation as NavigationClassic } from '~/layout/navigation/Navigation' +import { organizationLogic } from 'scenes/organizationLogic' +import { sceneLogic } from 'scenes/sceneLogic' +import { LoadedScene } from 'scenes/sceneTypes' +import { userLogic } from 'scenes/userLogic' + import { ErrorBoundary } from '~/layout/ErrorBoundary' +import { GlobalModals } from '~/layout/GlobalModals' import { breadcrumbsLogic } from '~/layout/navigation/Breadcrumbs/breadcrumbsLogic' -import { organizationLogic } from 'scenes/organizationLogic' -import { ToastCloseButton } from 'lib/lemon-ui/lemonToast' -import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' -import { inAppPromptLogic } from 'lib/logic/inAppPrompt/inAppPromptLogic' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { FEATURE_FLAGS } from 'lib/constants' +import { Navigation as NavigationClassic } from '~/layout/navigation/Navigation' import { Navigation as Navigation3000 } from '~/layout/navigation-3000/Navigation' -import { useEffect } from 'react' import { themeLogic } from '~/layout/navigation-3000/themeLogic' -import { GlobalModals } from '~/layout/GlobalModals' import { actionsModel } from '~/models/actionsModel' import { cohortsModel } from '~/models/cohortsModel' +import type { appLogicType } from './AppType' +import { preflightLogic } from './PreflightCheck/preflightLogic' +import { teamLogic } from './teamLogic' + export const appLogic = kea([ path(['scenes', 'App']), connect([teamLogic, organizationLogic, frontendAppsLogic, inAppPromptLogic, actionsModel, cohortsModel]), diff --git a/frontend/src/scenes/IntegrationsRedirect/IntegrationsRedirect.tsx b/frontend/src/scenes/IntegrationsRedirect/IntegrationsRedirect.tsx index df3757fcfcdf5..3a1cb65525eb6 100644 --- a/frontend/src/scenes/IntegrationsRedirect/IntegrationsRedirect.tsx +++ b/frontend/src/scenes/IntegrationsRedirect/IntegrationsRedirect.tsx @@ -1,5 +1,5 @@ -import { SceneExport } from 'scenes/sceneTypes' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { SceneExport } from 'scenes/sceneTypes' import { integrationsLogic } from 'scenes/settings/project/integrationsLogic' export const scene: SceneExport = { diff --git a/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx b/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx index dfbadfeb38a13..fab3d72dbe6c0 100644 --- a/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx +++ b/frontend/src/scenes/PreflightCheck/PreflightCheck.tsx @@ -1,23 +1,25 @@ -import { useValues, useActions } from 'kea' -import { PreflightCheckStatus, PreflightItem, preflightLogic } from './preflightLogic' import './PreflightCheck.scss' -import { capitalizeFirstLetter } from 'lib/utils' -import { SceneExport } from 'scenes/sceneTypes' -import { LemonButton } from 'lib/lemon-ui/LemonButton' + +import { Link, Spinner } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { IconCheckCircleOutline, IconErrorOutline, + IconRefresh, IconUnfoldLess, IconUnfoldMore, - IconRefresh, IconWarning, } from 'lib/lemon-ui/icons' -import clsx from 'clsx' -import { LemonRow } from 'lib/lemon-ui/LemonRow' -import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' -import { Link, Spinner } from '@posthog/lemon-ui' +import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { capitalizeFirstLetter } from 'lib/utils' +import { SceneExport } from 'scenes/sceneTypes' + +import { PreflightCheckStatus, PreflightItem, preflightLogic } from './preflightLogic' export const scene: SceneExport = { component: PreflightCheck, diff --git a/frontend/src/scenes/PreflightCheck/preflightLogic.test.ts b/frontend/src/scenes/PreflightCheck/preflightLogic.test.ts index 5ae4c1839df05..9ef3b88c8c0a8 100644 --- a/frontend/src/scenes/PreflightCheck/preflightLogic.test.ts +++ b/frontend/src/scenes/PreflightCheck/preflightLogic.test.ts @@ -1,7 +1,9 @@ import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' import { urls } from 'scenes/urls' + import { initKeaTests } from '~/test/init' + import { preflightLogic } from './preflightLogic' describe('preflightLogic', () => { diff --git a/frontend/src/scenes/PreflightCheck/preflightLogic.tsx b/frontend/src/scenes/PreflightCheck/preflightLogic.tsx index 2c71e29c70192..c57b8a20178b8 100644 --- a/frontend/src/scenes/PreflightCheck/preflightLogic.tsx +++ b/frontend/src/scenes/PreflightCheck/preflightLogic.tsx @@ -1,12 +1,14 @@ import { actions, events, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { actionToUrl, router, urlToAction } from 'kea-router' import api from 'lib/api' -import { PreflightStatus, Realm } from '~/types' -import posthog from 'posthog-js' import { getAppContext } from 'lib/utils/getAppContext' -import type { preflightLogicType } from './preflightLogicType' +import posthog from 'posthog-js' import { urls } from 'scenes/urls' -import { actionToUrl, router, urlToAction } from 'kea-router' -import { loaders } from 'kea-loaders' + +import { PreflightStatus, Realm } from '~/types' + +import type { preflightLogicType } from './preflightLogicType' export type PreflightMode = 'experimentation' | 'live' diff --git a/frontend/src/scenes/ResourcePermissionModal.tsx b/frontend/src/scenes/ResourcePermissionModal.tsx index 00dd60a034956..4e81f46ccc99b 100644 --- a/frontend/src/scenes/ResourcePermissionModal.tsx +++ b/frontend/src/scenes/ResourcePermissionModal.tsx @@ -1,13 +1,15 @@ import { LemonButton, LemonModal, LemonTable } from '@posthog/lemon-ui' import { useValues } from 'kea' +import { TitleWithIcon } from 'lib/components/TitleWithIcon' import { IconDelete, IconSettings } from 'lib/lemon-ui/icons' import { LemonSelectMultiple, LemonSelectMultipleOptionItem, } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { TitleWithIcon } from 'lib/components/TitleWithIcon' + import { AccessLevel, Resource, RoleType } from '~/types' + import { FormattedResourceLevel, permissionsLogic, diff --git a/frontend/src/scenes/Unsubscribe/Unsubscribe.tsx b/frontend/src/scenes/Unsubscribe/Unsubscribe.tsx index 2b2d2426633f8..b7658b413e04f 100644 --- a/frontend/src/scenes/Unsubscribe/Unsubscribe.tsx +++ b/frontend/src/scenes/Unsubscribe/Unsubscribe.tsx @@ -1,8 +1,9 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { unsubscribeLogic } from './unsubscribeLogic' import { useValues } from 'kea' import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { SceneExport } from 'scenes/sceneTypes' + +import { unsubscribeLogic } from './unsubscribeLogic' export const scene: SceneExport = { component: Unsubscribe, diff --git a/frontend/src/scenes/UpgradeModal.tsx b/frontend/src/scenes/UpgradeModal.tsx index bd10d36595fc1..ea065da4c6a3b 100644 --- a/frontend/src/scenes/UpgradeModal.tsx +++ b/frontend/src/scenes/UpgradeModal.tsx @@ -2,6 +2,7 @@ import { LemonButton, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { capitalizeFirstLetter } from 'lib/utils' import { posthog } from 'posthog-js' + import { sceneLogic } from './sceneLogic' import { urls } from './urls' diff --git a/frontend/src/scenes/actions/Action.tsx b/frontend/src/scenes/actions/Action.tsx index e4e7b934b4f3d..61b1ccb3d2f0c 100644 --- a/frontend/src/scenes/actions/Action.tsx +++ b/frontend/src/scenes/actions/Action.tsx @@ -1,14 +1,16 @@ -import { ActionEdit } from './ActionEdit' import { useActions, useValues } from 'kea' import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import { ActionType } from '~/types' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { SceneExport } from 'scenes/sceneTypes' import { actionLogic, ActionLogicProps } from 'scenes/actions/actionLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { Query } from '~/queries/Query/Query' import { NodeKind } from '~/queries/schema' -import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' +import { ActionType } from '~/types' + +import { ActionEdit } from './ActionEdit' export const scene: SceneExport = { logic: actionLogic, diff --git a/frontend/src/scenes/actions/ActionEdit.tsx b/frontend/src/scenes/actions/ActionEdit.tsx index e7a3182f8ee7d..14f524ee0e958 100644 --- a/frontend/src/scenes/actions/ActionEdit.tsx +++ b/frontend/src/scenes/actions/ActionEdit.tsx @@ -1,25 +1,27 @@ -import { compactNumber, uuid } from 'lib/utils' -import { Link } from 'lib/lemon-ui/Link' +import { LemonTextArea } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { actionEditLogic, ActionEditLogicProps } from './actionEditLogic' -import { ActionStep } from './ActionStep' +import { Form } from 'kea-forms' import { combineUrl, router } from 'kea-router' -import { PageHeader } from 'lib/components/PageHeader' -import { teamLogic } from 'scenes/teamLogic' -import { urls } from 'scenes/urls' import { EditableField } from 'lib/components/EditableField/EditableField' -import { ActionStepType, AvailableFeature } from '~/types' -import { userLogic } from 'scenes/userLogic' import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { PageHeader } from 'lib/components/PageHeader' import { Field } from 'lib/forms/Field' +import { IconInfo, IconPlayCircle, IconPlus, IconWarning } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { Form } from 'kea-forms' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { IconInfo, IconPlayCircle, IconPlus, IconWarning } from 'lib/lemon-ui/icons' -import { tagsModel } from '~/models/tagsModel' +import { Link } from 'lib/lemon-ui/Link' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { LemonTextArea } from '@posthog/lemon-ui' +import { compactNumber, uuid } from 'lib/utils' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { tagsModel } from '~/models/tagsModel' +import { ActionStepType, AvailableFeature } from '~/types' + +import { actionEditLogic, ActionEditLogicProps } from './actionEditLogic' +import { ActionStep } from './ActionStep' export function ActionEdit({ action: loadedAction, id, onSave, temporaryToken }: ActionEditLogicProps): JSX.Element { const logicProps: ActionEditLogicProps = { diff --git a/frontend/src/scenes/actions/ActionStep.tsx b/frontend/src/scenes/actions/ActionStep.tsx index c51064e66b171..3bab146512df3 100644 --- a/frontend/src/scenes/actions/ActionStep.tsx +++ b/frontend/src/scenes/actions/ActionStep.tsx @@ -1,18 +1,20 @@ -import { LemonEventName } from './EventName' -import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { URL_MATCHING_HINTS } from 'scenes/actions/hints' -import { ActionStepType, StringMatching } from '~/types' +import './ActionStep.scss' + import { LemonButton, LemonInput, LemonSegmentedButton, Link } from '@posthog/lemon-ui' -import { IconClose, IconOpenInApp } from 'lib/lemon-ui/icons' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { OperandTag } from 'lib/components/PropertyFilters/components/OperandTag' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { IconClose, IconOpenInApp } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' import { useState } from 'react' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { URL_MATCHING_HINTS } from 'scenes/actions/hints' -import './ActionStep.scss' -import { OperandTag } from 'lib/components/PropertyFilters/components/OperandTag' +import { ActionStepType, StringMatching } from '~/types' + +import { LemonEventName } from './EventName' const learnMoreLink = 'https://posthog.com/docs/user-guides/actions?utm_medium=in-product&utm_campaign=action-page' diff --git a/frontend/src/scenes/actions/EventName.tsx b/frontend/src/scenes/actions/EventName.tsx index b467e2d044028..e5ddd0f0e1c6c 100644 --- a/frontend/src/scenes/actions/EventName.tsx +++ b/frontend/src/scenes/actions/EventName.tsx @@ -1,6 +1,6 @@ import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' interface LemonEventNamePropsWithoutAllEvents { value: string diff --git a/frontend/src/scenes/actions/NewActionButton.tsx b/frontend/src/scenes/actions/NewActionButton.tsx index 4d98c98b77253..4ad666f60f5b8 100644 --- a/frontend/src/scenes/actions/NewActionButton.tsx +++ b/frontend/src/scenes/actions/NewActionButton.tsx @@ -1,11 +1,11 @@ -import { useState } from 'react' +import { LemonModal } from '@posthog/lemon-ui' import { router } from 'kea-router' -import { urls } from 'scenes/urls' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' import { IconEdit, IconMagnifier } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonModal } from '@posthog/lemon-ui' +import { useState } from 'react' +import { urls } from 'scenes/urls' export function NewActionButton({ onSelectOption }: { onSelectOption?: () => void }): JSX.Element { const [visible, setVisible] = useState(false) diff --git a/frontend/src/scenes/actions/actionEditLogic.tsx b/frontend/src/scenes/actions/actionEditLogic.tsx index 980461d56e80c..a32cd4a3a44c4 100644 --- a/frontend/src/scenes/actions/actionEditLogic.tsx +++ b/frontend/src/scenes/actions/actionEditLogic.tsx @@ -1,20 +1,22 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers } from 'kea' +import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' +import { beforeUnload, router, urlToAction } from 'kea-router' import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { Link } from 'lib/lemon-ui/Link' import { uuid } from 'lib/utils' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { actionsModel } from '~/models/actionsModel' -import type { actionEditLogicType } from './actionEditLogicType' -import { ActionStepType, ActionType } from '~/types' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { loaders } from 'kea-loaders' -import { forms } from 'kea-forms' -import { beforeUnload, router, urlToAction } from 'kea-router' -import { urls } from 'scenes/urls' import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' -import { Link } from 'lib/lemon-ui/Link' -import { tagsModel } from '~/models/tagsModel' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { actionsModel } from '~/models/actionsModel' +import { tagsModel } from '~/models/tagsModel' +import { ActionStepType, ActionType } from '~/types' + +import type { actionEditLogicType } from './actionEditLogicType' export type NewActionType = Partial & Pick diff --git a/frontend/src/scenes/actions/actionLogic.ts b/frontend/src/scenes/actions/actionLogic.ts index 6b47df87c05a7..7aa1934af84ce 100644 --- a/frontend/src/scenes/actions/actionLogic.ts +++ b/frontend/src/scenes/actions/actionLogic.ts @@ -1,11 +1,13 @@ +import { actions, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, actions, reducers, selectors, listeners, events } from 'kea' import api from 'lib/api' -import type { actionLogicType } from './actionLogicType' -import { ActionType, Breadcrumb } from '~/types' -import { urls } from 'scenes/urls' -import { Scene } from 'scenes/sceneTypes' import { DataManagementTab } from 'scenes/data-management/DataManagementScene' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { ActionType, Breadcrumb } from '~/types' + +import type { actionLogicType } from './actionLogicType' export interface ActionLogicProps { id?: ActionType['id'] diff --git a/frontend/src/scenes/actions/actionsLogic.ts b/frontend/src/scenes/actions/actionsLogic.ts index f150c85111a64..063ba899d5d38 100644 --- a/frontend/src/scenes/actions/actionsLogic.ts +++ b/frontend/src/scenes/actions/actionsLogic.ts @@ -1,16 +1,17 @@ -import { kea, selectors, path, actions, reducers, connect } from 'kea' -import { ActionType, Breadcrumb, ProductKey } from '~/types' -import { urls } from 'scenes/urls' - -import type { actionsLogicType } from './actionsLogicType' -import { actionsModel } from '~/models/actionsModel' import Fuse from 'fuse.js' -import { userLogic } from 'scenes/userLogic' +import { actions, connect, kea, path, reducers, selectors } from 'kea' import { subscriptions } from 'kea-subscriptions' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' -import { Scene } from 'scenes/sceneTypes' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { DataManagementTab } from 'scenes/data-management/DataManagementScene' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { actionsModel } from '~/models/actionsModel' +import { ActionType, Breadcrumb, ProductKey } from '~/types' + +import type { actionsLogicType } from './actionsLogicType' export type ActionsFilterType = 'all' | 'me' diff --git a/frontend/src/scenes/annotations/AnnotationModal.tsx b/frontend/src/scenes/annotations/AnnotationModal.tsx index a697a84456ce0..ad4edf2b00b23 100644 --- a/frontend/src/scenes/annotations/AnnotationModal.tsx +++ b/frontend/src/scenes/annotations/AnnotationModal.tsx @@ -1,14 +1,16 @@ import { LemonButton, LemonModal, LemonModalProps, LemonSelect, LemonTextArea, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' -import { Field } from 'lib/forms/Field' import { DatePicker } from 'lib/components/DatePicker' -import { annotationScopeToName, annotationModalLogic, ANNOTATION_DAYJS_FORMAT } from './annotationModalLogic' -import { AnnotationScope } from '~/types' +import { Field } from 'lib/forms/Field' import { IconWarning } from 'lib/lemon-ui/icons' import { shortTimeZone } from 'lib/utils' import { urls } from 'scenes/urls' +import { AnnotationScope } from '~/types' + +import { ANNOTATION_DAYJS_FORMAT, annotationModalLogic, annotationScopeToName } from './annotationModalLogic' + export function NewAnnotationButton(): JSX.Element { const { openModalToCreateAnnotation } = useActions(annotationModalLogic) return ( diff --git a/frontend/src/scenes/annotations/Annotations.stories.tsx b/frontend/src/scenes/annotations/Annotations.stories.tsx index 8a99d5c64f7a5..79550faaba47b 100644 --- a/frontend/src/scenes/annotations/Annotations.stories.tsx +++ b/frontend/src/scenes/annotations/Annotations.stories.tsx @@ -1,9 +1,11 @@ -import { useEffect } from 'react' import { Meta } from '@storybook/react' -import { App } from 'scenes/App' import { router } from 'kea-router' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' + import annotations from './__mocks__/annotations.json' const meta: Meta = { diff --git a/frontend/src/scenes/annotations/Annotations.tsx b/frontend/src/scenes/annotations/Annotations.tsx index b71b8969d0899..974de8f172104 100644 --- a/frontend/src/scenes/annotations/Annotations.tsx +++ b/frontend/src/scenes/annotations/Annotations.tsx @@ -1,26 +1,28 @@ -import { useValues, useActions } from 'kea' -import { - annotationScopeToLevel, - annotationScopeToName, - annotationModalLogic, - ANNOTATION_DAYJS_FORMAT, -} from './annotationModalLogic' -import { AnnotationScope, InsightShortId, AnnotationType, ProductKey } from '~/types' -import { LemonTable, LemonTableColumns, LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { createdAtColumn } from 'lib/lemon-ui/LemonTable/columnUtils' +import { Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { MicrophoneHog } from 'lib/components/hedgehogs' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { IconEdit } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { createdAtColumn } from 'lib/lemon-ui/LemonTable/columnUtils' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { IconEdit } from 'lib/lemon-ui/icons' -import { Link } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { teamLogic } from 'scenes/teamLogic' +import { shortTimeZone } from 'lib/utils' import { organizationLogic } from 'scenes/organizationLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { AnnotationScope, AnnotationType, InsightShortId, ProductKey } from '~/types' + import { AnnotationModal } from './AnnotationModal' -import { shortTimeZone } from 'lib/utils' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' -import { MicrophoneHog } from 'lib/components/hedgehogs' +import { + ANNOTATION_DAYJS_FORMAT, + annotationModalLogic, + annotationScopeToLevel, + annotationScopeToName, +} from './annotationModalLogic' export function Annotations(): JSX.Element { const { currentTeam } = useValues(teamLogic) diff --git a/frontend/src/scenes/annotations/annotationModalLogic.ts b/frontend/src/scenes/annotations/annotationModalLogic.ts index d1dd40c68061e..74b32ef8d58a1 100644 --- a/frontend/src/scenes/annotations/annotationModalLogic.ts +++ b/frontend/src/scenes/annotations/annotationModalLogic.ts @@ -1,16 +1,18 @@ import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' -import api from 'lib/api' -import { AnnotationScope, AnnotationType, InsightModel, ProductKey } from '~/types' import { forms } from 'kea-forms' -import { dayjs, Dayjs } from 'lib/dayjs' -import { annotationsModel, deserializeAnnotation } from '~/models/annotationsModel' -import type { annotationModalLogicType } from './annotationModalLogicType' -import { teamLogic } from 'scenes/teamLogic' +import { urlToAction } from 'kea-router' +import api from 'lib/api' import { FEATURE_FLAGS } from 'lib/constants' -import { userLogic } from 'scenes/userLogic' +import { Dayjs, dayjs } from 'lib/dayjs' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { urlToAction } from 'kea-router' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { annotationsModel, deserializeAnnotation } from '~/models/annotationsModel' +import { AnnotationScope, AnnotationType, InsightModel, ProductKey } from '~/types' + +import type { annotationModalLogicType } from './annotationModalLogicType' export const ANNOTATION_DAYJS_FORMAT = 'MMMM DD, YYYY h:mm A' diff --git a/frontend/src/scenes/appContextLogic.ts b/frontend/src/scenes/appContextLogic.ts index 4386021d2e927..ce3a6a11a317c 100644 --- a/frontend/src/scenes/appContextLogic.ts +++ b/frontend/src/scenes/appContextLogic.ts @@ -1,14 +1,14 @@ +import * as Sentry from '@sentry/react' import { afterMount, connect, kea, path } from 'kea' import api from 'lib/api' import { getAppContext } from 'lib/utils/getAppContext' -import * as Sentry from '@sentry/react' -import { userLogic } from './userLogic' +import { UserType } from '~/types' import type { appContextLogicType } from './appContextLogicType' import { organizationLogic } from './organizationLogic' import { teamLogic } from './teamLogic' -import { UserType } from '~/types' +import { userLogic } from './userLogic' export const appContextLogic = kea([ path(['scenes', 'appContextLogic']), diff --git a/frontend/src/scenes/appScenes.ts b/frontend/src/scenes/appScenes.ts index ad997bb2635a3..8c7a8c5ab8c09 100644 --- a/frontend/src/scenes/appScenes.ts +++ b/frontend/src/scenes/appScenes.ts @@ -1,5 +1,5 @@ -import { Scene } from 'scenes/sceneTypes' import { preloadedScenes } from 'scenes/scenes' +import { Scene } from 'scenes/sceneTypes' export const appScenes: Record any> = { [Scene.Error404]: () => ({ default: preloadedScenes[Scene.Error404].component }), diff --git a/frontend/src/scenes/apps/AppLogsTab.tsx b/frontend/src/scenes/apps/AppLogsTab.tsx index 1bd8a87d36d71..440cf8dc8dcce 100644 --- a/frontend/src/scenes/apps/AppLogsTab.tsx +++ b/frontend/src/scenes/apps/AppLogsTab.tsx @@ -1,8 +1,9 @@ -import { appMetricsSceneLogic } from './appMetricsSceneLogic' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { useValues } from 'kea' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { PluginLogs } from 'scenes/plugins/plugin/PluginLogs' +import { appMetricsSceneLogic } from './appMetricsSceneLogic' + export function AppLogsTab(): JSX.Element { const { activeTab, pluginConfig, pluginConfigLoading } = useValues(appMetricsSceneLogic) diff --git a/frontend/src/scenes/apps/AppMetricsGraph.tsx b/frontend/src/scenes/apps/AppMetricsGraph.tsx index f816ce47b66ea..d72280f043770 100644 --- a/frontend/src/scenes/apps/AppMetricsGraph.tsx +++ b/frontend/src/scenes/apps/AppMetricsGraph.tsx @@ -1,14 +1,16 @@ -import { useEffect, useRef } from 'react' -import { getColorVar } from 'lib/colors' +import './AppMetricsGraph.scss' + import { Chart, ChartDataset, ChartItem } from 'lib/Chart' -import { DescriptionColumns } from './constants' +import { getColorVar } from 'lib/colors' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' - -import './AppMetricsGraph.scss' import { inStorybookTestRunner, lightenDarkenColor } from 'lib/utils' -import { AppMetrics } from './appMetricsSceneLogic' +import { useEffect, useRef } from 'react' + import { AppMetricsTab } from '~/types' +import { AppMetrics } from './appMetricsSceneLogic' +import { DescriptionColumns } from './constants' + export interface AppMetricsGraphProps { tab: AppMetricsTab metrics?: AppMetrics | null @@ -32,21 +34,21 @@ export function AppMetricsGraph({ tab, metrics, metricsLoading }: AppMetricsGrap label: descriptions.successes, data: metrics.successes, borderColor: '', - ...colorConfig('data-brand-blue'), + ...colorConfig('data-color-1'), }, ...(descriptions.successes_on_retry ? [ { label: descriptions.successes_on_retry, data: metrics.successes_on_retry, - ...colorConfig('data-yellow'), + ...colorConfig('data-color-13'), }, ] : []), { label: descriptions.failures, data: metrics.failures, - ...colorConfig('data-vermilion'), + ...colorConfig('data-color-5'), }, ], }, diff --git a/frontend/src/scenes/apps/AppMetricsScene.stories.tsx b/frontend/src/scenes/apps/AppMetricsScene.stories.tsx index 638bb317fc462..645f275c49214 100644 --- a/frontend/src/scenes/apps/AppMetricsScene.stories.tsx +++ b/frontend/src/scenes/apps/AppMetricsScene.stories.tsx @@ -1,12 +1,14 @@ import { Meta, Story } from '@storybook/react' -import { App } from 'scenes/App' -import { useEffect } from 'react' import { router } from 'kea-router' -import { mswDecorator } from '~/mocks/browser' -import { AppMetricsResponse } from './appMetricsSceneLogic' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' -import { AvailableFeature } from '~/types' + +import { mswDecorator } from '~/mocks/browser' import { useAvailableFeatures } from '~/mocks/features' +import { AvailableFeature } from '~/types' + +import { AppMetricsResponse } from './appMetricsSceneLogic' const meta: Meta = { title: 'Scenes-App/Apps/App Metrics', diff --git a/frontend/src/scenes/apps/AppMetricsScene.tsx b/frontend/src/scenes/apps/AppMetricsScene.tsx index 7b4c62f3d1b83..e129ff60be3b0 100644 --- a/frontend/src/scenes/apps/AppMetricsScene.tsx +++ b/frontend/src/scenes/apps/AppMetricsScene.tsx @@ -1,22 +1,24 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { appMetricsSceneLogic } from 'scenes/apps/appMetricsSceneLogic' -import { PageHeader } from 'lib/components/PageHeader' -import { useValues, useActions } from 'kea' -import { MetricsTab } from './MetricsTab' -import { HistoricalExportsTab } from './HistoricalExportsTab' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { ErrorDetailsModal } from './ErrorDetailsModal' +import { LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { PageHeader } from 'lib/components/PageHeader' +import { IconSettings } from 'lib/lemon-ui/icons' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { PluginTags } from 'scenes/plugins/tabs/apps/components' +import { appMetricsSceneLogic } from 'scenes/apps/appMetricsSceneLogic' import { PluginImage } from 'scenes/plugins/plugin/PluginImage' -import { AppLogsTab } from './AppLogsTab' -import { LemonButton } from '@posthog/lemon-ui' -import { IconSettings } from 'lib/lemon-ui/icons' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginTags } from 'scenes/plugins/tabs/apps/components' +import { SceneExport } from 'scenes/sceneTypes' + import { AppMetricsTab } from '~/types' +import { AppLogsTab } from './AppLogsTab' +import { ErrorDetailsModal } from './ErrorDetailsModal' +import { HistoricalExportsTab } from './HistoricalExportsTab' +import { MetricsTab } from './MetricsTab' + export const scene: SceneExport = { component: AppMetrics, logic: appMetricsSceneLogic, diff --git a/frontend/src/scenes/apps/ErrorDetailsModal.tsx b/frontend/src/scenes/apps/ErrorDetailsModal.tsx index 926177cd552fb..e4c643a5b422b 100644 --- a/frontend/src/scenes/apps/ErrorDetailsModal.tsx +++ b/frontend/src/scenes/apps/ErrorDetailsModal.tsx @@ -1,13 +1,14 @@ -import { useState } from 'react' import { useActions, useValues } from 'kea' -import { AppMetricErrorDetail, appMetricsSceneLogic } from './appMetricsSceneLogic' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { TZLabel } from 'lib/components/TZLabel' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconChevronLeft, IconChevronRight, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { useState } from 'react' + +import { AppMetricErrorDetail, appMetricsSceneLogic } from './appMetricsSceneLogic' export function ErrorDetailsModal(): JSX.Element { const { errorDetails, errorDetailsModalError, errorDetailsLoading } = useValues(appMetricsSceneLogic) diff --git a/frontend/src/scenes/apps/FrontendAppScene.tsx b/frontend/src/scenes/apps/FrontendAppScene.tsx index 84bb63518c32f..bc0f2146fdf6f 100644 --- a/frontend/src/scenes/apps/FrontendAppScene.tsx +++ b/frontend/src/scenes/apps/FrontendAppScene.tsx @@ -1,8 +1,8 @@ -import { SceneExport } from 'scenes/sceneTypes' import { useValues } from 'kea' -import { frontendAppSceneLogic } from 'scenes/apps/frontendAppSceneLogic' import { PageHeader } from 'lib/components/PageHeader' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { frontendAppSceneLogic } from 'scenes/apps/frontendAppSceneLogic' +import { SceneExport } from 'scenes/sceneTypes' export function FrontendAppScene(): JSX.Element { const { Component, appConfig, breadcrumbs } = useValues(frontendAppSceneLogic) diff --git a/frontend/src/scenes/apps/HistoricalExport.tsx b/frontend/src/scenes/apps/HistoricalExport.tsx index 0642110b3b3c9..2a9415e75146f 100644 --- a/frontend/src/scenes/apps/HistoricalExport.tsx +++ b/frontend/src/scenes/apps/HistoricalExport.tsx @@ -1,9 +1,11 @@ import { Card } from 'antd' import { useValues } from 'kea' + +import { AppMetricsTab } from '~/types' + import { AppMetricsGraph } from './AppMetricsGraph' import { historicalExportLogic, HistoricalExportLogicProps } from './historicalExportLogic' import { ErrorsOverview, MetricsOverview } from './MetricsTab' -import { AppMetricsTab } from '~/types' export function HistoricalExport(props: HistoricalExportLogicProps): JSX.Element { const { data, dataLoading } = useValues(historicalExportLogic(props)) diff --git a/frontend/src/scenes/apps/HistoricalExportsTab.tsx b/frontend/src/scenes/apps/HistoricalExportsTab.tsx index ed94dcbeb1433..974337a54cb48 100644 --- a/frontend/src/scenes/apps/HistoricalExportsTab.tsx +++ b/frontend/src/scenes/apps/HistoricalExportsTab.tsx @@ -1,15 +1,16 @@ +import { Progress } from 'antd' import { useActions, useValues } from 'kea' -import { appMetricsSceneLogic, HistoricalExportInfo } from './appMetricsSceneLogic' +import { LemonButton } from 'lib/lemon-ui/LemonButton/LemonButton' import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { HistoricalExport } from './HistoricalExport' import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { Progress } from 'antd' -import { PluginJobModal } from 'scenes/plugins/edit/interface-jobs/PluginJobConfiguration' import { useEffect } from 'react' -import { LemonButton } from 'lib/lemon-ui/LemonButton/LemonButton' +import { PluginJobModal } from 'scenes/plugins/edit/interface-jobs/PluginJobConfiguration' import { userLogic } from 'scenes/userLogic' +import { appMetricsSceneLogic, HistoricalExportInfo } from './appMetricsSceneLogic' +import { HistoricalExport } from './HistoricalExport' + const RELOAD_HISTORICAL_EXPORTS_FREQUENCY_MS = 20000 export function HistoricalExportsTab(): JSX.Element { diff --git a/frontend/src/scenes/apps/MetricsTab.tsx b/frontend/src/scenes/apps/MetricsTab.tsx index 425219384c912..635039f37377a 100644 --- a/frontend/src/scenes/apps/MetricsTab.tsx +++ b/frontend/src/scenes/apps/MetricsTab.tsx @@ -1,17 +1,19 @@ -import { AppErrorSummary, AppMetrics, appMetricsSceneLogic } from './appMetricsSceneLogic' -import { DescriptionColumns } from './constants' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { humanFriendlyDuration, humanFriendlyNumber } from 'lib/utils' -import { AppMetricsGraph } from './AppMetricsGraph' -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' import { useActions, useValues } from 'kea' -import { LemonTable } from 'lib/lemon-ui/LemonTable' import { TZLabel } from 'lib/components/TZLabel' +import { IconInfo } from 'lib/lemon-ui/icons' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { LemonTable } from 'lib/lemon-ui/LemonTable' import { Link } from 'lib/lemon-ui/Link' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { IconInfo } from 'lib/lemon-ui/icons' +import { humanFriendlyDuration, humanFriendlyNumber } from 'lib/utils' + import { AppMetricsTab } from '~/types' +import { AppMetricsGraph } from './AppMetricsGraph' +import { AppErrorSummary, AppMetrics, appMetricsSceneLogic } from './appMetricsSceneLogic' +import { DescriptionColumns } from './constants' + export interface MetricsTabProps { tab: AppMetricsTab } diff --git a/frontend/src/scenes/apps/appMetricsSceneLogic.ts b/frontend/src/scenes/apps/appMetricsSceneLogic.ts index 577b3624e6287..ca4b4239691c5 100644 --- a/frontend/src/scenes/apps/appMetricsSceneLogic.ts +++ b/frontend/src/scenes/apps/appMetricsSceneLogic.ts @@ -1,18 +1,19 @@ -import { kea, key, props, path, actions, selectors, reducers, listeners } from 'kea' +import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' - -import type { appMetricsSceneLogicType } from './appMetricsSceneLogicType' -import { urls } from 'scenes/urls' -import { AppMetricsTab, AppMetricsUrlParams, Breadcrumb, PluginConfigWithPluginInfo, UserBasicType } from '~/types' -import api, { PaginatedResponse } from 'lib/api' -import { teamLogic } from 'scenes/teamLogic' import { actionToUrl, urlToAction } from 'kea-router' +import { router } from 'kea-router' +import api, { PaginatedResponse } from 'lib/api' +import { dayjs } from 'lib/dayjs' import { toParams } from 'lib/utils' import { HISTORICAL_EXPORT_JOB_NAME_V2 } from 'scenes/plugins/edit/interface-jobs/PluginJobConfiguration' -import { interfaceJobsLogic, InterfaceJobsProps } from '../plugins/edit/interface-jobs/interfaceJobsLogic' -import { dayjs } from 'lib/dayjs' -import { router } from 'kea-router' import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { AppMetricsTab, AppMetricsUrlParams, Breadcrumb, PluginConfigWithPluginInfo, UserBasicType } from '~/types' + +import { interfaceJobsLogic, InterfaceJobsProps } from '../plugins/edit/interface-jobs/interfaceJobsLogic' +import type { appMetricsSceneLogicType } from './appMetricsSceneLogicType' export interface AppMetricsLogicProps { /** Used as the logic's key */ diff --git a/frontend/src/scenes/apps/frontendAppRequire.ts b/frontend/src/scenes/apps/frontendAppRequire.ts index f0df491e00ee4..2714d4e0c20c0 100644 --- a/frontend/src/scenes/apps/frontendAppRequire.ts +++ b/frontend/src/scenes/apps/frontendAppRequire.ts @@ -1,11 +1,11 @@ +import * as appsCommon from '@posthog/apps-common' +import * as lemonUi from '@posthog/lemon-ui' import * as allKea from 'kea' -import * as allKeaRouter from 'kea-router' -import * as allKeaLoaders from 'kea-loaders' import * as allKeaForms from 'kea-forms' -import * as allKeaWindowValues from 'kea-window-values' +import * as allKeaLoaders from 'kea-loaders' +import * as allKeaRouter from 'kea-router' import * as allKeaSubscriptions from 'kea-subscriptions' -import * as appsCommon from '@posthog/apps-common' -import * as lemonUi from '@posthog/lemon-ui' +import * as allKeaWindowValues from 'kea-window-values' import React from 'react' const packages = { diff --git a/frontend/src/scenes/apps/frontendAppSceneLogic.ts b/frontend/src/scenes/apps/frontendAppSceneLogic.ts index a165a79be9482..d60075bb66c84 100644 --- a/frontend/src/scenes/apps/frontendAppSceneLogic.ts +++ b/frontend/src/scenes/apps/frontendAppSceneLogic.ts @@ -1,9 +1,11 @@ -import { BuiltLogic, connect, kea, key, LogicWrapper, props, selectors, path } from 'kea' +import { BuiltLogic, connect, kea, key, LogicWrapper, path, props, selectors } from 'kea' +import { subscriptions } from 'kea-subscriptions' +import { objectsEqual } from 'lib/utils' import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' + import { Breadcrumb, FrontendApp, FrontendAppConfig } from '~/types' + import type { frontendAppSceneLogicType } from './frontendAppSceneLogicType' -import { subscriptions } from 'kea-subscriptions' -import { objectsEqual } from 'lib/utils' export interface FrontendAppSceneLogicProps { /** Used as the logic's key */ @@ -15,9 +17,9 @@ export const frontendAppSceneLogic = kea([ path(['scenes', 'apps', 'frontendAppSceneLogic']), props({} as FrontendAppSceneLogicProps), key((props) => props.id), - connect({ + connect(() => ({ values: [frontendAppsLogic, ['frontendApps', 'appConfigs']], - }), + })), selectors(() => ({ // Frontend app created after receiving a bundle via import('').getFrontendApp() frontendApp: [ diff --git a/frontend/src/scenes/apps/frontendAppsLogic.tsx b/frontend/src/scenes/apps/frontendAppsLogic.tsx index 1405c620eed87..a8e24d32a3dfd 100644 --- a/frontend/src/scenes/apps/frontendAppsLogic.tsx +++ b/frontend/src/scenes/apps/frontendAppsLogic.tsx @@ -1,13 +1,15 @@ import { actions, afterMount, connect, defaults, kea, path, reducers } from 'kea' -import type { frontendAppsLogicType } from './frontendAppsLogicType' -import { getAppContext } from 'lib/utils/getAppContext' import { loaders } from 'kea-loaders' -import { FrontendApp, FrontendAppConfig } from '~/types' -import { frontendAppRequire } from './frontendAppRequire' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { getAppContext } from 'lib/utils/getAppContext' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { urls } from 'scenes/urls' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' + +import { FrontendApp, FrontendAppConfig } from '~/types' + +import { frontendAppRequire } from './frontendAppRequire' +import type { frontendAppsLogicType } from './frontendAppsLogicType' /** Manages the loading and lifecycle of frontend apps. */ export const frontendAppsLogic = kea([ diff --git a/frontend/src/scenes/apps/historicalExportLogic.ts b/frontend/src/scenes/apps/historicalExportLogic.ts index 80fdfe5735f82..710a90823f067 100644 --- a/frontend/src/scenes/apps/historicalExportLogic.ts +++ b/frontend/src/scenes/apps/historicalExportLogic.ts @@ -1,9 +1,9 @@ -import { kea, events, key, props, path } from 'kea' +import { events, kea, key, path, props } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' + import { teamLogic } from '../teamLogic' import { AppErrorSummary, AppMetrics, HistoricalExportInfo } from './appMetricsSceneLogic' - import type { historicalExportLogicType } from './historicalExportLogicType' export interface HistoricalExportLogicProps { diff --git a/frontend/src/scenes/authentication/InviteSignup.stories.tsx b/frontend/src/scenes/authentication/InviteSignup.stories.tsx index 2f138aefa6c66..6cc336caf2d18 100644 --- a/frontend/src/scenes/authentication/InviteSignup.stories.tsx +++ b/frontend/src/scenes/authentication/InviteSignup.stories.tsx @@ -1,8 +1,10 @@ // Signup.stories.tsx import { Meta } from '@storybook/react' import { useEffect } from 'react' + import { mswDecorator, useStorybookMocks } from '~/mocks/browser' import preflightJson from '~/mocks/fixtures/_preflight.json' + import { InviteSignup } from './InviteSignup' import { inviteSignupLogic } from './inviteSignupLogic' diff --git a/frontend/src/scenes/authentication/InviteSignup.tsx b/frontend/src/scenes/authentication/InviteSignup.tsx index 3c4c780eb3f6e..00705231015a4 100644 --- a/frontend/src/scenes/authentication/InviteSignup.tsx +++ b/frontend/src/scenes/authentication/InviteSignup.tsx @@ -1,22 +1,24 @@ +import { LemonButton, LemonCheckbox, LemonDivider, LemonInput } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { inviteSignupLogic, ErrorCodes } from './inviteSignupLogic' -import { userLogic } from 'scenes/userLogic' -import { PrevalidatedInvite } from '~/types' -import { Link } from 'lib/lemon-ui/Link' +import { Form } from 'kea-forms' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import PasswordStrength from 'lib/components/PasswordStrength' +import SignupRoleSelect from 'lib/components/SignupRoleSelect' import { SocialLoginButtons } from 'lib/components/SocialLoginButton/SocialLoginButton' -import { urls } from 'scenes/urls' -import { SceneExport } from 'scenes/sceneTypes' +import { Field, PureField } from 'lib/forms/Field' +import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { IconChevronLeft, IconChevronRight } from 'lib/lemon-ui/icons' -import { LemonButton, LemonCheckbox, LemonDivider, LemonInput } from '@posthog/lemon-ui' -import { Form } from 'kea-forms' -import { Field, PureField } from 'lib/forms/Field' -import PasswordStrength from 'lib/components/PasswordStrength' -import clsx from 'clsx' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import SignupRoleSelect from 'lib/components/SignupRoleSelect' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { PrevalidatedInvite } from '~/types' + +import { ErrorCodes, inviteSignupLogic } from './inviteSignupLogic' import { SupportModalButton } from './SupportModalButton' export const scene: SceneExport = { diff --git a/frontend/src/scenes/authentication/Login.stories.tsx b/frontend/src/scenes/authentication/Login.stories.tsx index f6ffca2742686..8c92d465c6143 100644 --- a/frontend/src/scenes/authentication/Login.stories.tsx +++ b/frontend/src/scenes/authentication/Login.stories.tsx @@ -1,13 +1,15 @@ // Login.stories.tsx import { Meta, StoryFn } from '@storybook/react' -import { Login } from './Login' -import { mswDecorator, useStorybookMocks } from '~/mocks/browser' -import { useEffect } from 'react' -import preflightJson from '../../mocks/fixtures/_preflight.json' import { router } from 'kea-router' +import { useEffect } from 'react' import { urls } from 'scenes/urls' -import { loginLogic } from './loginLogic' + +import { mswDecorator, useStorybookMocks } from '~/mocks/browser' + +import preflightJson from '../../mocks/fixtures/_preflight.json' +import { Login } from './Login' import { Login2FA } from './Login2FA' +import { loginLogic } from './loginLogic' const meta: Meta = { title: 'Scenes-Other/Login', diff --git a/frontend/src/scenes/authentication/Login.tsx b/frontend/src/scenes/authentication/Login.tsx index 61a13b54feb53..6e3045f29b51b 100644 --- a/frontend/src/scenes/authentication/Login.tsx +++ b/frontend/src/scenes/authentication/Login.tsx @@ -1,21 +1,24 @@ -import { useEffect, useRef } from 'react' import './Login.scss' -import { useActions, useValues } from 'kea' -import { loginLogic } from './loginLogic' -import { Link } from 'lib/lemon-ui/Link' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { SocialLoginButtons, SSOEnforcedLoginButton } from 'lib/components/SocialLoginButton/SocialLoginButton' -import clsx from 'clsx' -import { SceneExport } from 'scenes/sceneTypes' + import { LemonButton, LemonInput } from '@posthog/lemon-ui' +import { captureException } from '@sentry/react' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import { SocialLoginButtons, SSOEnforcedLoginButton } from 'lib/components/SocialLoginButton/SocialLoginButton' import { Field } from 'lib/forms/Field' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' -import RegionSelect from './RegionSelect' +import { Link } from 'lib/lemon-ui/Link' +import { useEffect, useRef } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { loginLogic } from './loginLogic' import { redirectIfLoggedInOtherInstance } from './redirectToLoggedInInstance' -import { captureException } from '@sentry/react' +import RegionSelect from './RegionSelect' import { SupportModalButton } from './SupportModalButton' +import { useButtonStyle } from './useButtonStyles' export const ERROR_MESSAGES: Record = { no_new_organizations: @@ -55,6 +58,7 @@ export function Login(): JSX.Element { const passwordInputRef = useRef(null) const isPasswordHidden = precheckResponse.status === 'pending' || precheckResponse.sso_enforcement + const buttonStyles = useButtonStyle() useEffect(() => { if (preflight?.cloud) { @@ -146,6 +150,7 @@ export function Login(): JSX.Element { type="primary" center loading={isLoginSubmitting || precheckResponseLoading} + {...buttonStyles} > Log in diff --git a/frontend/src/scenes/authentication/Login2FA.tsx b/frontend/src/scenes/authentication/Login2FA.tsx index 6bd87ca93a34b..c1bb53c1e59d4 100644 --- a/frontend/src/scenes/authentication/Login2FA.tsx +++ b/frontend/src/scenes/authentication/Login2FA.tsx @@ -1,15 +1,19 @@ +import { LemonButton, LemonInput } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { login2FALogic } from './login2FALogic' import { Form } from 'kea-forms' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { Field } from 'lib/forms/Field' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonButton, LemonInput } from '@posthog/lemon-ui' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { login2FALogic } from './login2FALogic' +import { useButtonStyle } from './useButtonStyles' export function Login2FA(): JSX.Element { const { isTwofactortokenSubmitting, generalError } = useValues(login2FALogic) const { preflight } = useValues(preflightLogic) + const buttonStyles = useButtonStyle() + return ( Login diff --git a/frontend/src/scenes/authentication/PasswordReset.stories.tsx b/frontend/src/scenes/authentication/PasswordReset.stories.tsx index 6837428f76278..4b3c6b4cdd399 100644 --- a/frontend/src/scenes/authentication/PasswordReset.stories.tsx +++ b/frontend/src/scenes/authentication/PasswordReset.stories.tsx @@ -1,10 +1,12 @@ // PasswordReset.stories.tsx import { Meta } from '@storybook/react' -import { PasswordReset } from './PasswordReset' import { useEffect } from 'react' +import { passwordResetLogic } from 'scenes/authentication/passwordResetLogic' + import { useStorybookMocks } from '~/mocks/browser' import preflightJson from '~/mocks/fixtures/_preflight.json' -import { passwordResetLogic } from 'scenes/authentication/passwordResetLogic' + +import { PasswordReset } from './PasswordReset' // some metadata and optional parameters const meta: Meta = { diff --git a/frontend/src/scenes/authentication/PasswordReset.tsx b/frontend/src/scenes/authentication/PasswordReset.tsx index d1c2b3a037314..a3a7590cd48c9 100644 --- a/frontend/src/scenes/authentication/PasswordReset.tsx +++ b/frontend/src/scenes/authentication/PasswordReset.tsx @@ -1,19 +1,21 @@ /* Scene to request a password reset email. */ -import { useActions, useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { passwordResetLogic } from './passwordResetLogic' -import { router } from 'kea-router' -import { SceneExport } from 'scenes/sceneTypes' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { LemonButton, LemonDivider, LemonInput, Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' -import { Field } from 'lib/forms/Field' +import { router } from 'kea-router' import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' +import { Field } from 'lib/forms/Field' import { IconCheckCircleOutline, IconErrorOutline } from 'lib/lemon-ui/icons' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { passwordResetLogic } from './passwordResetLogic' import { SupportModalButton } from './SupportModalButton' +import { useButtonStyle } from './useButtonStyles' export const scene: SceneExport = { component: PasswordReset, @@ -85,6 +87,7 @@ function EmailUnavailable(): JSX.Element { function ResetForm(): JSX.Element { const { isRequestPasswordResetSubmitting } = useValues(passwordResetLogic) + const buttonStyles = useButtonStyle() return (
    @@ -108,6 +111,7 @@ function ResetForm(): JSX.Element { htmlType="submit" data-attr="password-reset" loading={isRequestPasswordResetSubmitting} + {...buttonStyles} > Continue @@ -118,13 +122,21 @@ function ResetForm(): JSX.Element { function ResetSuccess(): JSX.Element { const { requestPasswordReset } = useValues(passwordResetLogic) const { push } = useActions(router) + const buttonStyles = useButtonStyle() return (
    Request received successfully! If the email {requestPasswordReset?.email || 'you typed'} exists, you’ll receive an email with a reset link soon.
    - push('/login')}> + push('/login')} + {...buttonStyles} + > Back to login
    @@ -135,6 +147,7 @@ function ResetSuccess(): JSX.Element { function ResetThrottled(): JSX.Element { const { requestPasswordReset } = useValues(passwordResetLogic) const { push } = useActions(router) + const buttonStyles = useButtonStyle() return (
    @@ -145,7 +158,14 @@ function ResetThrottled(): JSX.Element { {' '} if you think this has been a mistake.
    - push('/login')}> + push('/login')} + {...buttonStyles} + > Back to login
    diff --git a/frontend/src/scenes/authentication/PasswordResetComplete.stories.tsx b/frontend/src/scenes/authentication/PasswordResetComplete.stories.tsx index d6c551b553594..7387cc6a3b7b0 100644 --- a/frontend/src/scenes/authentication/PasswordResetComplete.stories.tsx +++ b/frontend/src/scenes/authentication/PasswordResetComplete.stories.tsx @@ -1,11 +1,13 @@ // PasswordResetComplete.stories.tsx import { Meta } from '@storybook/react' -import { PasswordResetComplete } from './PasswordResetComplete' -import { useEffect } from 'react' import { router } from 'kea-router' +import { useEffect } from 'react' import { urls } from 'scenes/urls' + import { useStorybookMocks } from '~/mocks/browser' +import { PasswordResetComplete } from './PasswordResetComplete' + // some metadata and optional parameters const meta: Meta = { title: 'Scenes-Other/Password Reset Complete', diff --git a/frontend/src/scenes/authentication/PasswordResetComplete.tsx b/frontend/src/scenes/authentication/PasswordResetComplete.tsx index 27a32962d2961..49e1a4e93742d 100644 --- a/frontend/src/scenes/authentication/PasswordResetComplete.tsx +++ b/frontend/src/scenes/authentication/PasswordResetComplete.tsx @@ -1,17 +1,18 @@ /* Scene to enter a new password from a received reset link */ -import { useValues } from 'kea' -import { passwordResetLogic } from './passwordResetLogic' -import { SceneExport } from 'scenes/sceneTypes' -import { Field } from 'lib/forms/Field' import { LemonButton, LemonInput } from '@posthog/lemon-ui' -import PasswordStrength from 'lib/components/PasswordStrength' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { useValues } from 'kea' import { Form } from 'kea-forms' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import PasswordStrength from 'lib/components/PasswordStrength' +import { Field } from 'lib/forms/Field' import { IconErrorOutline } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { SceneExport } from 'scenes/sceneTypes' + +import { passwordResetLogic } from './passwordResetLogic' export const scene: SceneExport = { component: PasswordResetComplete, diff --git a/frontend/src/scenes/authentication/RegionSelect.tsx b/frontend/src/scenes/authentication/RegionSelect.tsx index c380579f55d7c..161ced67a365d 100644 --- a/frontend/src/scenes/authentication/RegionSelect.tsx +++ b/frontend/src/scenes/authentication/RegionSelect.tsx @@ -1,14 +1,14 @@ -import { useState } from 'react' -import { useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { LemonModal, LemonSelect } from '@posthog/lemon-ui' -import { Region } from '~/types' -import { CLOUD_HOSTNAMES, FEATURE_FLAGS } from 'lib/constants' +import { useValues } from 'kea' import { router } from 'kea-router' - +import { CLOUD_HOSTNAMES, FEATURE_FLAGS } from 'lib/constants' import { PureField } from 'lib/forms/Field' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { IconCheckmark } from 'lib/lemon-ui/icons' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { useState } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { Region } from '~/types' const sections = [ { diff --git a/frontend/src/scenes/authentication/Setup2FA.tsx b/frontend/src/scenes/authentication/Setup2FA.tsx index 8c9360d8a01ee..dcc3113d18e44 100644 --- a/frontend/src/scenes/authentication/Setup2FA.tsx +++ b/frontend/src/scenes/authentication/Setup2FA.tsx @@ -1,10 +1,12 @@ -import { setup2FALogic } from './setup2FALogic' +import './Setup2FA.scss' + import { LemonButton, LemonInput } from '@posthog/lemon-ui' +import { useValues } from 'kea' import { Form } from 'kea-forms' import { Field } from 'lib/forms/Field' -import { useValues } from 'kea' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import './Setup2FA.scss' + +import { setup2FALogic } from './setup2FALogic' export function Setup2FA({ onSuccess }: { onSuccess: () => void }): JSX.Element | null { const { startSetupLoading, generalError } = useValues(setup2FALogic({ onSuccess })) diff --git a/frontend/src/scenes/authentication/SupportModalButton.tsx b/frontend/src/scenes/authentication/SupportModalButton.tsx index 557b894d609f3..1beaed9a86e02 100644 --- a/frontend/src/scenes/authentication/SupportModalButton.tsx +++ b/frontend/src/scenes/authentication/SupportModalButton.tsx @@ -1,7 +1,7 @@ import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { SupportModal } from 'lib/components/Support/SupportModal' import { supportLogic } from 'lib/components/Support/supportLogic' +import { SupportModal } from 'lib/components/Support/SupportModal' import { IconBugShield } from 'lib/lemon-ui/icons' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' diff --git a/frontend/src/scenes/authentication/WelcomeLogo.tsx b/frontend/src/scenes/authentication/WelcomeLogo.tsx index ac96c6f39989a..74f86005ab386 100644 --- a/frontend/src/scenes/authentication/WelcomeLogo.tsx +++ b/frontend/src/scenes/authentication/WelcomeLogo.tsx @@ -1,9 +1,9 @@ +import { Link } from '@posthog/lemon-ui' +import { useValues } from 'kea' +import defaultLogo from 'public/posthog-logo.svg' import cloudLogo from 'public/posthog-logo-cloud.svg' import demoLogo from 'public/posthog-logo-demo.svg' -import defaultLogo from 'public/posthog-logo.svg' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { useValues } from 'kea' -import { Link } from '@posthog/lemon-ui' export function WelcomeLogo({ view }: { view?: string }): JSX.Element { const UTM_TAGS = `utm_campaign=in-product&utm_tag=${view || 'welcome'}-header` diff --git a/frontend/src/scenes/authentication/inviteSignupLogic.ts b/frontend/src/scenes/authentication/inviteSignupLogic.ts index 23f70644c1ef6..4950cac2750ca 100644 --- a/frontend/src/scenes/authentication/inviteSignupLogic.ts +++ b/frontend/src/scenes/authentication/inviteSignupLogic.ts @@ -1,9 +1,11 @@ -import { kea, path, actions, reducers, listeners } from 'kea' +import { actions, kea, listeners, path, reducers } from 'kea' +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { urlToAction } from 'kea-router' -import { forms } from 'kea-forms' import api from 'lib/api' + import { PrevalidatedInvite } from '~/types' + import type { inviteSignupLogicType } from './inviteSignupLogicType' export enum ErrorCodes { diff --git a/frontend/src/scenes/authentication/login2FALogic.ts b/frontend/src/scenes/authentication/login2FALogic.ts index 796dfc764f764..4da5deb50adbe 100644 --- a/frontend/src/scenes/authentication/login2FALogic.ts +++ b/frontend/src/scenes/authentication/login2FALogic.ts @@ -1,12 +1,13 @@ -import { kea, path, connect, listeners, actions, reducers } from 'kea' +import { actions, connect, kea, listeners, path, reducers } from 'kea' import { forms } from 'kea-forms' import api from 'lib/api' -import { SSOProvider } from '~/types' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { handleLoginRedirect } from './loginLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + +import { SSOProvider } from '~/types' import type { login2FALogicType } from './login2FALogicType' +import { handleLoginRedirect } from './loginLogic' export interface AuthenticateResponseType { success: boolean diff --git a/frontend/src/scenes/authentication/loginLogic.test.ts b/frontend/src/scenes/authentication/loginLogic.test.ts index 063c22f5df8cf..de694690d4298 100644 --- a/frontend/src/scenes/authentication/loginLogic.test.ts +++ b/frontend/src/scenes/authentication/loginLogic.test.ts @@ -1,8 +1,9 @@ -import { handleLoginRedirect, loginLogic } from 'scenes/authentication/loginLogic' -import { initKeaTests } from '~/test/init' import { router } from 'kea-router' -import { initKea } from '~/initKea' import { testUtilsPlugin } from 'kea-test-utils' +import { handleLoginRedirect, loginLogic } from 'scenes/authentication/loginLogic' + +import { initKea } from '~/initKea' +import { initKeaTests } from '~/test/init' describe('loginLogic', () => { describe('parseLoginRedirectURL', () => { diff --git a/frontend/src/scenes/authentication/loginLogic.ts b/frontend/src/scenes/authentication/loginLogic.ts index ca159812bc771..419f04e3c91d6 100644 --- a/frontend/src/scenes/authentication/loginLogic.ts +++ b/frontend/src/scenes/authentication/loginLogic.ts @@ -1,16 +1,18 @@ -import { kea, path, connect, listeners, actions, reducers } from 'kea' +import { actions, connect, kea, listeners, path, reducers } from 'kea' +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { urlToAction } from 'kea-router' -import { forms } from 'kea-forms' -import api from 'lib/api' -import type { loginLogicType } from './loginLogicType' import { router } from 'kea-router' -import { SSOProvider } from '~/types' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import api from 'lib/api' import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { urls } from 'scenes/urls' +import { SSOProvider } from '~/types' + +import type { loginLogicType } from './loginLogicType' + export interface AuthenticateResponseType { success: boolean errorCode?: string diff --git a/frontend/src/scenes/authentication/passwordResetLogic.ts b/frontend/src/scenes/authentication/passwordResetLogic.ts index b4835fcce495e..487510514e474 100644 --- a/frontend/src/scenes/authentication/passwordResetLogic.ts +++ b/frontend/src/scenes/authentication/passwordResetLogic.ts @@ -1,9 +1,10 @@ import { kea, path, reducers } from 'kea' +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { urlToAction } from 'kea-router' -import { forms } from 'kea-forms' import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' + import type { passwordResetLogicType } from './passwordResetLogicType' export interface ResponseType { diff --git a/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts b/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts index 479a9a50f4b78..b5e5bf852f8ec 100644 --- a/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts +++ b/frontend/src/scenes/authentication/redirectToLoggedInInstance.ts @@ -22,8 +22,8 @@ */ import { lemonToast } from '@posthog/lemon-ui' -import { getCookie } from 'lib/api' import { captureException } from '@sentry/react' +import { getCookie } from 'lib/api' // cookie values const PH_CURRENT_INSTANCE = 'ph_current_instance' diff --git a/frontend/src/scenes/authentication/setup2FALogic.ts b/frontend/src/scenes/authentication/setup2FALogic.ts index b76de829f2cdb..7fe843a95fdaf 100644 --- a/frontend/src/scenes/authentication/setup2FALogic.ts +++ b/frontend/src/scenes/authentication/setup2FALogic.ts @@ -1,10 +1,10 @@ -import { kea, path, connect, afterMount, listeners, actions, reducers, props } from 'kea' -import { loaders } from 'kea-loaders' +import { lemonToast } from '@posthog/lemon-ui' +import { actions, afterMount, connect, kea, listeners, path, props, reducers } from 'kea' import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' import api from 'lib/api' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { lemonToast } from '@posthog/lemon-ui' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import type { setup2FALogicType } from './setup2FALogicType' diff --git a/frontend/src/scenes/authentication/signup/Signup.stories.tsx b/frontend/src/scenes/authentication/signup/Signup.stories.tsx index 0ebdd83f170fd..7fe843e9433c2 100644 --- a/frontend/src/scenes/authentication/signup/Signup.stories.tsx +++ b/frontend/src/scenes/authentication/signup/Signup.stories.tsx @@ -1,9 +1,11 @@ // Signup.stories.tsx import { Meta } from '@storybook/react' import { useEffect } from 'react' -import { mswDecorator, useStorybookMocks } from '~/mocks/browser' import { userLogic } from 'scenes/userLogic' + +import { mswDecorator, useStorybookMocks } from '~/mocks/browser' import preflightJson from '~/mocks/fixtures/_preflight.json' + import { SignupContainer } from './SignupContainer' const meta: Meta = { diff --git a/frontend/src/scenes/authentication/signup/SignupContainer.tsx b/frontend/src/scenes/authentication/signup/SignupContainer.tsx index 6dce9f08abe6d..1fea3ff456e73 100644 --- a/frontend/src/scenes/authentication/signup/SignupContainer.tsx +++ b/frontend/src/scenes/authentication/signup/SignupContainer.tsx @@ -1,15 +1,17 @@ import { useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { userLogic } from 'scenes/userLogic' -import { SceneExport } from 'scenes/sceneTypes' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' -import { SignupForm } from './signupForm/SignupForm' -import { Region } from '~/types' import { router } from 'kea-router' -import { Link } from 'lib/lemon-ui/Link' -import { IconCheckCircleOutline } from 'lib/lemon-ui/icons' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { CLOUD_HOSTNAMES, FEATURE_FLAGS } from 'lib/constants' +import { IconCheckCircleOutline } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { userLogic } from 'scenes/userLogic' + +import { Region } from '~/types' + +import { SignupForm } from './signupForm/SignupForm' export const scene: SceneExport = { component: SignupContainer, diff --git a/frontend/src/scenes/authentication/signup/signupForm/SignupForm.tsx b/frontend/src/scenes/authentication/signup/signupForm/SignupForm.tsx index 897fa059db68d..3569e017d4645 100644 --- a/frontend/src/scenes/authentication/signup/signupForm/SignupForm.tsx +++ b/frontend/src/scenes/authentication/signup/signupForm/SignupForm.tsx @@ -1,15 +1,16 @@ -import { useEffect, useState } from 'react' -import { useActions, useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { signupLogic } from './signupLogic' -import { userLogic } from '../../../userLogic' -import { SceneExport } from 'scenes/sceneTypes' import { LemonButton } from '@posthog/lemon-ui' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { useActions, useValues } from 'kea' import { IconArrowLeft } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { useEffect, useState } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { userLogic } from '../../../userLogic' import { SignupPanel1 } from './panels/SignupPanel1' import { SignupPanel2 } from './panels/SignupPanel2' +import { signupLogic } from './signupLogic' export const scene: SceneExport = { component: SignupForm, diff --git a/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel1.tsx b/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel1.tsx index f950fb7ae2a39..088644afa997d 100644 --- a/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel1.tsx +++ b/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel1.tsx @@ -1,18 +1,21 @@ -import { useRef, useEffect } from 'react' -import { LemonInput, LemonButton } from '@posthog/lemon-ui' +import { LemonButton, LemonInput } from '@posthog/lemon-ui' import { useValues } from 'kea' import { Form } from 'kea-forms' -import { Field } from 'lib/forms/Field' import PasswordStrength from 'lib/components/PasswordStrength' import { SocialLoginButtons } from 'lib/components/SocialLoginButton/SocialLoginButton' +import { Field } from 'lib/forms/Field' +import { Link } from 'lib/lemon-ui/Link' +import { useEffect, useRef } from 'react' +import { useButtonStyle } from 'scenes/authentication/useButtonStyles' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { signupLogic } from '../signupLogic' -import { Link } from 'lib/lemon-ui/Link' export function SignupPanel1(): JSX.Element | null { const { preflight } = useValues(preflightLogic) const { isSignupPanel1Submitting, signupPanel1 } = useValues(signupLogic) const emailInputRef = useRef(null) + const buttonStyles = useButtonStyle() useEffect(() => { // There's no password in the demo environment @@ -71,6 +74,7 @@ export function SignupPanel1(): JSX.Element | null { data-attr="signup-start" loading={isSignupPanel1Submitting} disabled={isSignupPanel1Submitting} + {...buttonStyles} > Continue diff --git a/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel2.tsx b/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel2.tsx index 07ea86b6bd31a..a6d893ca97537 100644 --- a/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel2.tsx +++ b/frontend/src/scenes/authentication/signup/signupForm/panels/SignupPanel2.tsx @@ -1,17 +1,20 @@ -import { LemonInput, LemonButton, Link } from '@posthog/lemon-ui' +import { LemonButton, LemonInput, Link } from '@posthog/lemon-ui' import { useValues } from 'kea' import { Form } from 'kea-forms' +import SignupReferralSource from 'lib/components/SignupReferralSource' import SignupRoleSelect from 'lib/components/SignupRoleSelect' import { Field } from 'lib/forms/Field' +import { useButtonStyle } from 'scenes/authentication/useButtonStyles' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { signupLogic } from '../signupLogic' -import SignupReferralSource from 'lib/components/SignupReferralSource' const UTM_TAGS = 'utm_campaign=in-product&utm_tag=signup-header' export function SignupPanel2(): JSX.Element | null { const { preflight } = useValues(preflightLogic) const { isSignupPanel2Submitting } = useValues(signupLogic) + const buttonStyles = useButtonStyle() return (
    @@ -44,6 +47,7 @@ export function SignupPanel2(): JSX.Element | null { data-attr="signup-submit" loading={isSignupPanel2Submitting} disabled={isSignupPanel2Submitting} + {...buttonStyles} > {!preflight?.demo ? 'Create account' diff --git a/frontend/src/scenes/authentication/signup/signupForm/signupLogic.ts b/frontend/src/scenes/authentication/signup/signupForm/signupLogic.ts index 3806235fe9939..803f47ecad608 100644 --- a/frontend/src/scenes/authentication/signup/signupForm/signupLogic.ts +++ b/frontend/src/scenes/authentication/signup/signupForm/signupLogic.ts @@ -1,14 +1,15 @@ -import { kea, path, connect, actions, reducers } from 'kea' -import { urlToAction } from 'kea-router' +import { lemonToast } from '@posthog/lemon-ui' +import { isString } from '@tiptap/core' +import { actions, connect, kea, path, reducers } from 'kea' import { forms } from 'kea-forms' +import { urlToAction } from 'kea-router' import api from 'lib/api' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import type { signupLogicType } from './signupLogicType' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { CLOUD_HOSTNAMES, FEATURE_FLAGS } from 'lib/constants' -import { lemonToast } from '@posthog/lemon-ui' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { urls } from 'scenes/urls' -import { isString } from '@tiptap/core' + +import type { signupLogicType } from './signupLogicType' export interface AccountResponse { success: boolean diff --git a/frontend/src/scenes/authentication/signup/verify-email/VerifyEmail.tsx b/frontend/src/scenes/authentication/signup/verify-email/VerifyEmail.tsx index e646a86352de5..723bb6061d41b 100644 --- a/frontend/src/scenes/authentication/signup/verify-email/VerifyEmail.tsx +++ b/frontend/src/scenes/authentication/signup/verify-email/VerifyEmail.tsx @@ -2,11 +2,12 @@ import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { BridgePage } from 'lib/components/BridgePage/BridgePage' import { HeartHog, MailHog, SurprisedHog } from 'lib/components/hedgehogs' +import { supportLogic } from 'lib/components/Support/supportLogic' +import { SupportModal } from 'lib/components/Support/SupportModal' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { SceneExport } from 'scenes/sceneTypes' + import { verifyEmailLogic } from './verifyEmailLogic' -import { SupportModal } from 'lib/components/Support/SupportModal' -import { supportLogic } from 'lib/components/Support/supportLogic' export const scene: SceneExport = { component: VerifyEmail, @@ -51,7 +52,7 @@ export function VerifyEmail(): JSX.Element { return (
    - +
    {view === 'pending' ? ( <> diff --git a/frontend/src/scenes/authentication/signup/verify-email/verifyEmailLogic.ts b/frontend/src/scenes/authentication/signup/verify-email/verifyEmailLogic.ts index 72c68e8b58423..8b28386fff3e3 100644 --- a/frontend/src/scenes/authentication/signup/verify-email/verifyEmailLogic.ts +++ b/frontend/src/scenes/authentication/signup/verify-email/verifyEmailLogic.ts @@ -3,6 +3,7 @@ import { loaders } from 'kea-loaders' import { urlToAction } from 'kea-router' import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' + import type { verifyEmailLogicType } from './verifyEmailLogicType' export interface ResponseType { diff --git a/frontend/src/scenes/authentication/useButtonStyles.ts b/frontend/src/scenes/authentication/useButtonStyles.ts new file mode 100644 index 0000000000000..9f678edaa2018 --- /dev/null +++ b/frontend/src/scenes/authentication/useButtonStyles.ts @@ -0,0 +1,13 @@ +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' + +export function useButtonStyle(): Record { + const is3000 = useFeatureFlag('POSTHOG_3000') + + return is3000 + ? { + size: 'large', + } + : { + size: 'medium', + } +} diff --git a/frontend/src/scenes/batch_exports/BatchExportBackfillModal.tsx b/frontend/src/scenes/batch_exports/BatchExportBackfillModal.tsx index c9a92bb9d1aa8..94d2416f57ca2 100644 --- a/frontend/src/scenes/batch_exports/BatchExportBackfillModal.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportBackfillModal.tsx @@ -1,12 +1,11 @@ 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 { Field } from 'lib/forms/Field' -import { batchExportLogic } from './batchExportLogic' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonCalendarSelectInput } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' +import { LemonModal } from 'lib/lemon-ui/LemonModal' + +import { batchExportLogic } from './batchExportLogic' export function BatchExportBackfillModal(): JSX.Element { const { batchExportConfig, isBackfillModalOpen, isBackfillFormSubmitting } = useValues(batchExportLogic) diff --git a/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx b/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx index 78a5d8d4dd2e6..e2774c7b38a98 100644 --- a/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx @@ -1,17 +1,18 @@ -import { LemonInput, LemonSelect, LemonCheckbox, LemonDivider, LemonButton } from '@posthog/lemon-ui' -import { useValues, useActions } from 'kea' +import { LemonButton, LemonCheckbox, LemonDivider, LemonInput, LemonSelect } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' +import { FEATURE_FLAGS } from 'lib/constants' +import { Field } from 'lib/forms/Field' +import { IconInfo } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonCalendarSelectInput } from 'lib/lemon-ui/LemonCalendar/LemonCalendarSelect' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { LemonFileInput } from 'lib/lemon-ui/LemonFileInput/LemonFileInput' -import { IconInfo } from 'lib/lemon-ui/icons' -import { BatchExportsEditLogicProps, batchExportsEditLogic } from './batchExportEditLogic' -import { Field } from 'lib/forms/Field' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' + +import { batchExportsEditLogic, BatchExportsEditLogicProps } from './batchExportEditLogic' export function BatchExportsEditForm(props: BatchExportsEditLogicProps): JSX.Element { const logic = batchExportsEditLogic(props) diff --git a/frontend/src/scenes/batch_exports/BatchExportEditScene.tsx b/frontend/src/scenes/batch_exports/BatchExportEditScene.tsx index 3f6dfcf661597..3c9e42f872ef8 100644 --- a/frontend/src/scenes/batch_exports/BatchExportEditScene.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportEditScene.tsx @@ -1,9 +1,10 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { PageHeader } from 'lib/components/PageHeader' import { useValues } from 'kea' -import { BatchExportsEditLogicProps, batchExportsEditLogic } from './batchExportEditLogic' -import { batchExportsEditSceneLogic } from './batchExportEditSceneLogic' +import { PageHeader } from 'lib/components/PageHeader' +import { SceneExport } from 'scenes/sceneTypes' + import { BatchExportsEditForm } from './BatchExportEditForm' +import { batchExportsEditLogic, BatchExportsEditLogicProps } from './batchExportEditLogic' +import { batchExportsEditSceneLogic } from './batchExportEditSceneLogic' export const scene: SceneExport = { component: BatchExportsEditScene, diff --git a/frontend/src/scenes/batch_exports/BatchExportScene.tsx b/frontend/src/scenes/batch_exports/BatchExportScene.tsx index f8f1b778ee6bf..244c1f55b236a 100644 --- a/frontend/src/scenes/batch_exports/BatchExportScene.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportScene.tsx @@ -1,36 +1,38 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { PageHeader } from 'lib/components/PageHeader' +import { TZLabel } from '@posthog/apps-common' import { LemonButton, + LemonCheckbox, LemonDivider, - LemonTable, - LemonTag, LemonInput, + LemonTable, LemonTableColumns, - LemonCheckbox, + LemonTag, } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' import { useActions, useValues } from 'kea' -import { useEffect, useState } from 'react' -import { BatchExportLogicProps, batchExportLogic, BatchExportTab } from './batchExportLogic' -import { BatchExportLogsProps, batchExportLogsLogic, LOGS_PORTION_LIMIT } from './batchExportLogsLogic' -import { BatchExportRunIcon, BatchExportTag } from './components' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { NotFound } from 'lib/components/NotFound' +import { PageHeader } from 'lib/components/PageHeader' +import { dayjs } from 'lib/dayjs' import { IconEllipsis, IconRefresh } from 'lib/lemon-ui/icons' -import { capitalizeFirstLetter, identifierToHuman } from 'lib/utils' -import { BatchExportBackfillModal } from './BatchExportBackfillModal' -import { humanizeDestination, intervalToFrequency, isRunInProgress } from './utils' -import { TZLabel } from '@posthog/apps-common' -import { Popover } from 'lib/lemon-ui/Popover' import { LemonCalendarRange } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRange' -import { NotFound } from 'lib/components/NotFound' -import { LemonMenu } from 'lib/lemon-ui/LemonMenu' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { LemonMenu } from 'lib/lemon-ui/LemonMenu' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { dayjs } from 'lib/dayjs' -import { BatchExportLogEntryLevel, BatchExportLogEntry } from '~/types' +import { Popover } from 'lib/lemon-ui/Popover' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter, identifierToHuman } from 'lib/utils' import { pluralize } from 'lib/utils' +import { useEffect, useState } from 'react' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { BatchExportLogEntry, BatchExportLogEntryLevel } from '~/types' + +import { BatchExportBackfillModal } from './BatchExportBackfillModal' +import { batchExportLogic, BatchExportLogicProps, BatchExportTab } from './batchExportLogic' +import { batchExportLogsLogic, BatchExportLogsProps, LOGS_PORTION_LIMIT } from './batchExportLogsLogic' +import { BatchExportRunIcon, BatchExportTag } from './components' +import { humanizeDestination, intervalToFrequency, isRunInProgress } from './utils' export const scene: SceneExport = { component: BatchExportScene, diff --git a/frontend/src/scenes/batch_exports/BatchExports.stories.tsx b/frontend/src/scenes/batch_exports/BatchExports.stories.tsx index cc4bbde8ba5ae..5b1c2f9966acf 100644 --- a/frontend/src/scenes/batch_exports/BatchExports.stories.tsx +++ b/frontend/src/scenes/batch_exports/BatchExports.stories.tsx @@ -1,9 +1,11 @@ import { Meta, StoryFn } from '@storybook/react' -import { App } from 'scenes/App' -import { useEffect } from 'react' import { router } from 'kea-router' -import { mswDecorator } from '~/mocks/browser' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' + import { createExportServiceHandlers } from './__mocks__/api-mocks' export default { diff --git a/frontend/src/scenes/batch_exports/BatchExportsListScene.tsx b/frontend/src/scenes/batch_exports/BatchExportsListScene.tsx index d1883ab08dbdc..228d2ae96a82d 100644 --- a/frontend/src/scenes/batch_exports/BatchExportsListScene.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportsListScene.tsx @@ -1,11 +1,12 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { PageHeader } from 'lib/components/PageHeader' import { LemonButton, LemonTable, Link } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' import { useActions, useValues } from 'kea' -import { batchExportsListLogic } from './batchExportsListLogic' -import { LemonMenu, LemonMenuItems } from 'lib/lemon-ui/LemonMenu' +import { PageHeader } from 'lib/components/PageHeader' import { IconEllipsis } from 'lib/lemon-ui/icons' +import { LemonMenu, LemonMenuItems } from 'lib/lemon-ui/LemonMenu' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { batchExportsListLogic } from './batchExportsListLogic' import { BatchExportRunIcon, BatchExportTag } from './components' export const scene: SceneExport = { diff --git a/frontend/src/scenes/batch_exports/__mocks__/api-mocks.ts b/frontend/src/scenes/batch_exports/__mocks__/api-mocks.ts index ab62ca297b39e..b56c742569c45 100644 --- a/frontend/src/scenes/batch_exports/__mocks__/api-mocks.ts +++ b/frontend/src/scenes/batch_exports/__mocks__/api-mocks.ts @@ -1,4 +1,5 @@ import { CountedPaginatedResponse } from 'lib/api' + import { BatchExportConfiguration } from '~/types' export const createExportServiceHandlers = ( diff --git a/frontend/src/scenes/batch_exports/batchExportEditLogic.ts b/frontend/src/scenes/batch_exports/batchExportEditLogic.ts index 0fa15c3651b99..c2b9620d09ac9 100644 --- a/frontend/src/scenes/batch_exports/batchExportEditLogic.ts +++ b/frontend/src/scenes/batch_exports/batchExportEditLogic.ts @@ -1,4 +1,10 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, selectors } from 'kea' +import { forms } from 'kea-forms' +import { beforeUnload, router } from 'kea-router' +import api from 'lib/api' +import { Dayjs, dayjs } from 'lib/dayjs' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { BatchExportConfiguration, @@ -11,15 +17,8 @@ import { Breadcrumb, } from '~/types' -import api from 'lib/api' -import { forms } from 'kea-forms' -import { urls } from 'scenes/urls' -import { beforeUnload, router } from 'kea-router' - import type { batchExportsEditLogicType } from './batchExportEditLogicType' -import { dayjs, Dayjs } from 'lib/dayjs' import { batchExportLogic } from './batchExportLogic' -import { Scene } from 'scenes/sceneTypes' export type BatchExportsEditLogicProps = { id: string diff --git a/frontend/src/scenes/batch_exports/batchExportEditSceneLogic.ts b/frontend/src/scenes/batch_exports/batchExportEditSceneLogic.ts index 9d9825dea2865..e0766163ed448 100644 --- a/frontend/src/scenes/batch_exports/batchExportEditSceneLogic.ts +++ b/frontend/src/scenes/batch_exports/batchExportEditSceneLogic.ts @@ -1,14 +1,12 @@ import { connect, kea, key, path, props, selectors } from 'kea' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { Breadcrumb } from '~/types' -import { urls } from 'scenes/urls' - -import { batchExportLogic } from './batchExportLogic' import { BatchExportsEditLogicProps } from './batchExportEditLogic' - import type { batchExportsEditSceneLogicType } from './batchExportEditSceneLogicType' -import { Scene } from 'scenes/sceneTypes' +import { batchExportLogic } from './batchExportLogic' export const batchExportsEditSceneLogic = kea([ props({} as BatchExportsEditLogicProps), diff --git a/frontend/src/scenes/batch_exports/batchExportLogic.ts b/frontend/src/scenes/batch_exports/batchExportLogic.ts index 6c0c5ca8fcec1..37cc5fc86e649 100644 --- a/frontend/src/scenes/batch_exports/batchExportLogic.ts +++ b/frontend/src/scenes/batch_exports/batchExportLogic.ts @@ -1,17 +1,16 @@ +import { lemonToast } from '@posthog/lemon-ui' import { actions, beforeUnmount, kea, key, listeners, path, props, reducers, selectors } from 'kea' - +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' -import { BatchExportConfiguration, BatchExportRun, Breadcrumb, GroupedBatchExportRuns } from '~/types' - +import { router } from 'kea-router' import api, { PaginatedResponse } from 'lib/api' - -import { lemonToast } from '@posthog/lemon-ui' -import { forms } from 'kea-forms' -import { dayjs, Dayjs } from 'lib/dayjs' +import { Dayjs, dayjs } from 'lib/dayjs' +import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + +import { BatchExportConfiguration, BatchExportRun, Breadcrumb, GroupedBatchExportRuns } from '~/types' + import type { batchExportLogicType } from './batchExportLogicType' -import { router } from 'kea-router' -import { Scene } from 'scenes/sceneTypes' export type BatchExportLogicProps = { id: string diff --git a/frontend/src/scenes/batch_exports/batchExportLogsLogic.ts b/frontend/src/scenes/batch_exports/batchExportLogsLogic.ts index b63e4f50abf20..d361b286bde6d 100644 --- a/frontend/src/scenes/batch_exports/batchExportLogsLogic.ts +++ b/frontend/src/scenes/batch_exports/batchExportLogsLogic.ts @@ -1,10 +1,11 @@ -import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import api from '~/lib/api' -import { BatchExportLogEntryLevel, BatchExportLogEntry } from '~/types' import { CheckboxValueType } from 'antd/lib/checkbox/Group' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { teamLogic } from 'scenes/teamLogic' +import api from '~/lib/api' +import { BatchExportLogEntry, BatchExportLogEntryLevel } from '~/types' + import type { batchExportLogsLogicType } from './batchExportLogsLogicType' export interface BatchExportLogsProps { diff --git a/frontend/src/scenes/batch_exports/batchExportsListLogic.ts b/frontend/src/scenes/batch_exports/batchExportsListLogic.ts index 5ac29c5336dcf..98171e05009ac 100644 --- a/frontend/src/scenes/batch_exports/batchExportsListLogic.ts +++ b/frontend/src/scenes/batch_exports/batchExportsListLogic.ts @@ -1,13 +1,12 @@ +import { lemonToast } from '@posthog/lemon-ui' import { actions, afterMount, beforeUnmount, kea, listeners, path, reducers, selectors } from 'kea' - import { loaders } from 'kea-loaders' -import { BatchExportConfiguration } from '~/types' - import api, { CountedPaginatedResponse } from 'lib/api' +import { PaginationManual } from 'lib/lemon-ui/PaginationControl' + +import { BatchExportConfiguration } from '~/types' import type { batchExportsListLogicType } from './batchExportsListLogicType' -import { PaginationManual } from 'lib/lemon-ui/PaginationControl' -import { lemonToast } from '@posthog/lemon-ui' const PAGE_SIZE = 10 // Refresh the current page of exports periodically to see whats up. diff --git a/frontend/src/scenes/batch_exports/components.tsx b/frontend/src/scenes/batch_exports/components.tsx index 5e3856ce78aee..7191de9f40685 100644 --- a/frontend/src/scenes/batch_exports/components.tsx +++ b/frontend/src/scenes/batch_exports/components.tsx @@ -1,9 +1,10 @@ +import './BatchExports.scss' + import { LemonTag } from '@posthog/lemon-ui' import clsx from 'clsx' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { BatchExportConfiguration, BatchExportRun } from '~/types' -import './BatchExports.scss' +import { BatchExportConfiguration, BatchExportRun } from '~/types' export function BatchExportTag({ batchExportConfig }: { batchExportConfig: BatchExportConfiguration }): JSX.Element { return ( diff --git a/frontend/src/scenes/billing/Billing.stories.tsx b/frontend/src/scenes/billing/Billing.stories.tsx index 3a43552e646c0..ab6cbae8ea895 100644 --- a/frontend/src/scenes/billing/Billing.stories.tsx +++ b/frontend/src/scenes/billing/Billing.stories.tsx @@ -1,9 +1,11 @@ import { Meta } from '@storybook/react' -import { Billing } from './Billing' -import { useStorybookMocks, mswDecorator } from '~/mocks/browser' -import preflightJson from '~/mocks/fixtures/_preflight.json' + +import { mswDecorator, useStorybookMocks } from '~/mocks/browser' import billingJson from '~/mocks/fixtures/_billing_v2.json' import billingJsonWithDiscount from '~/mocks/fixtures/_billing_v2_with_discount.json' +import preflightJson from '~/mocks/fixtures/_preflight.json' + +import { Billing } from './Billing' const meta: Meta = { title: 'Scenes-Other/Billing v2', diff --git a/frontend/src/scenes/billing/Billing.tsx b/frontend/src/scenes/billing/Billing.tsx index 5c02c43bb2dbf..63517aa1d99b4 100644 --- a/frontend/src/scenes/billing/Billing.tsx +++ b/frontend/src/scenes/billing/Billing.tsx @@ -1,23 +1,24 @@ -import { useEffect } from 'react' -import { billingLogic } from './billingLogic' import { LemonButton, LemonDivider, LemonInput, Link } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { Field, Form } from 'kea-forms' +import { PageHeader } from 'lib/components/PageHeader' +import { supportLogic } from 'lib/components/Support/supportLogic' +import { dayjs } from 'lib/dayjs' +import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { IconPlus } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { dayjs } from 'lib/dayjs' -import clsx from 'clsx' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { capitalizeFirstLetter } from 'lib/utils' -import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { useEffect } from 'react' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { SceneExport } from 'scenes/sceneTypes' + import { BillingHero } from './BillingHero' -import { PageHeader } from 'lib/components/PageHeader' +import { billingLogic } from './billingLogic' import { BillingProduct } from './BillingProduct' -import { IconPlus } from 'lib/lemon-ui/icons' -import { SceneExport } from 'scenes/sceneTypes' -import { supportLogic } from 'lib/components/Support/supportLogic' -import { Field, Form } from 'kea-forms' -import { Tooltip } from 'lib/lemon-ui/Tooltip' export const scene: SceneExport = { component: Billing, diff --git a/frontend/src/scenes/billing/BillingGauge.tsx b/frontend/src/scenes/billing/BillingGauge.tsx index bdc61e4437a0c..cb0b5f480da50 100644 --- a/frontend/src/scenes/billing/BillingGauge.tsx +++ b/frontend/src/scenes/billing/BillingGauge.tsx @@ -1,8 +1,9 @@ +import './BillingGauge.scss' + import clsx from 'clsx' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { compactNumber } from 'lib/utils' import { useMemo } from 'react' -import './BillingGauge.scss' type BillingGaugeItemProps = { width: string diff --git a/frontend/src/scenes/billing/BillingHero.tsx b/frontend/src/scenes/billing/BillingHero.tsx index 3dbe62ca6dc62..ca8e8170a5832 100644 --- a/frontend/src/scenes/billing/BillingHero.tsx +++ b/frontend/src/scenes/billing/BillingHero.tsx @@ -1,5 +1,6 @@ -import { BlushingHog } from 'lib/components/hedgehogs' import './BillingHero.scss' + +import { BlushingHog } from 'lib/components/hedgehogs' import useResizeObserver from 'use-resize-observer' export const BillingHero = (): JSX.Element => { diff --git a/frontend/src/scenes/billing/BillingLimitInput.tsx b/frontend/src/scenes/billing/BillingLimitInput.tsx index 6f1dd652a4acb..db519a096265a 100644 --- a/frontend/src/scenes/billing/BillingLimitInput.tsx +++ b/frontend/src/scenes/billing/BillingLimitInput.tsx @@ -1,18 +1,22 @@ -import { BillingProductV2AddonType, BillingProductV2Type, BillingV2TierType } from '~/types' -import { billingLogic } from './billingLogic' -import { convertAmountToUsage } from './billing-utils' +import { LemonButton, LemonInput } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { billingProductLogic } from './billingProductLogic' -import { LemonButton, LemonInput } from '@posthog/lemon-ui' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import clsx from 'clsx' +import { useRef } from 'react' + +import { BillingProductV2AddonType, BillingProductV2Type, BillingV2TierType } from '~/types' + +import { convertAmountToUsage } from './billing-utils' +import { billingLogic } from './billingLogic' +import { billingProductLogic } from './billingProductLogic' export const BillingLimitInput = ({ product }: { product: BillingProductV2Type }): JSX.Element | null => { + const limitInputRef = useRef(null) const { billing, billingLoading } = useValues(billingLogic) const { updateBillingLimits } = useActions(billingLogic) const { isEditingBillingLimit, showBillingLimitInput, billingLimitInput, customLimitUsd } = useValues( - billingProductLogic({ product }) + billingProductLogic({ product, billingLimitInputRef: limitInputRef }) ) const { setIsEditingBillingLimit, setBillingLimitInput } = useActions(billingProductLogic({ product })) @@ -78,7 +82,7 @@ export const BillingLimitInput = ({ product }: { product: BillingProductV2Type } return null } return ( -
    +
    {!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..80310d43f6f2c 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 @@ -118,22 +121,20 @@ function DashboardScene(): JSX.Element { DashboardPlacement.Export, DashboardPlacement.FeatureFlag, ].includes(placement) && ( -
    -
    - ( - <> - - {key} - - )} - /> -
    +
    + ( + <> + + {key} + + )} + /> = [ { 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 2f0fa2f4aa926..00889a9e1e5aa 100644 --- a/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx +++ b/frontend/src/scenes/dashboard/dashboards/DashboardsTable.tsx @@ -1,29 +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 { 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, IconPinOutline, IconPinFilled, IconShare } from 'lib/lemon-ui/icons' import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' +import { DashboardPrivilegeLevel } from 'lib/constants' +import { IconCottage, IconLock } from 'lib/lemon-ui/icons' +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) @@ -70,7 +73,7 @@ export function DashboardsTable({ : () => pinDashboard(id, DashboardEventSource.DashboardsList) } tooltip={pinned ? 'Unpin dashboard' : 'Pin dashboard'} - icon={pinned ? : } + icon={pinned ? : } /> ) }, @@ -215,28 +218,31 @@ export function DashboardsTable({ />
    - setFilters({ pinned: !filters.pinned })} - icon={} - > - Pinned - -
    -
    - setFilters({ shared: !filters.shared })} - icon={} - > - Shared - + Filter to: +
    + setFilters({ pinned: !filters.pinned })} + icon={} + > + Pinned + +
    +
    + setFilters({ shared: !filters.shared })} + icon={} + > + Shared + +
    Created by: 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: '', - }) - } - > -
    - -
    -
    - + + description="Start with recommended metrics for a web app" + template={{ name: 'Web App Dashboard', template: 'DEFAULT_APP' }} + />
    ) } + +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 diff --git a/frontend/src/scenes/data-management/definition/definitionEditLogic.ts b/frontend/src/scenes/data-management/definition/definitionEditLogic.ts index e77625178a9da..8b14c0f51d2a9 100644 --- a/frontend/src/scenes/data-management/definition/definitionEditLogic.ts +++ b/frontend/src/scenes/data-management/definition/definitionEditLogic.ts @@ -1,20 +1,22 @@ import { beforeUnmount, connect, kea, key, path, props } from 'kea' -import { Definition, EventDefinition, PropertyDefinition } from '~/types' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' +import { capitalizeFirstLetter } from 'lib/utils' import { definitionLogic, DefinitionLogicProps, DefinitionPageMode, } from 'scenes/data-management/definition/definitionLogic' -import type { definitionEditLogicType } from './definitionEditLogicType' -import { capitalizeFirstLetter } from 'lib/utils' import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' import { propertyDefinitionsTableLogic } from 'scenes/data-management/properties/propertyDefinitionsTableLogic' + +import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' import { tagsModel } from '~/models/tagsModel' +import { Definition, EventDefinition, PropertyDefinition } from '~/types' + +import type { definitionEditLogicType } from './definitionEditLogicType' export interface DefinitionEditLogicProps extends DefinitionLogicProps { definition: Definition diff --git a/frontend/src/scenes/data-management/definition/definitionLogic.test.ts b/frontend/src/scenes/data-management/definition/definitionLogic.test.ts index 9b3ef5531dcaf..faea186096541 100644 --- a/frontend/src/scenes/data-management/definition/definitionLogic.test.ts +++ b/frontend/src/scenes/data-management/definition/definitionLogic.test.ts @@ -1,10 +1,11 @@ -import { createNewDefinition, definitionLogic } from 'scenes/data-management/definition/definitionLogic' -import { initKeaTests } from '~/test/init' +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' +import { createNewDefinition, definitionLogic } from 'scenes/data-management/definition/definitionLogic' +import { urls } from 'scenes/urls' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' import { mockEventDefinitions, mockEventPropertyDefinition } from '~/test/mocks' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' describe('definitionLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/data-management/definition/definitionLogic.ts b/frontend/src/scenes/data-management/definition/definitionLogic.ts index a861464bb8746..0c9a97067499c 100644 --- a/frontend/src/scenes/data-management/definition/definitionLogic.ts +++ b/frontend/src/scenes/data-management/definition/definitionLogic.ts @@ -1,17 +1,19 @@ -import { actions, afterMount, kea, key, props, path, selectors, reducers, connect } from 'kea' -import { AvailableFeature, Breadcrumb, Definition, PropertyDefinition } from '~/types' +import { actions, afterMount, connect, kea, key, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import api from 'lib/api' -import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import type { definitionLogicType } from './definitionLogicType' +import api from 'lib/api' import { getPropertyLabel } from 'lib/taxonomy' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + +import { updatePropertyDefinitions } from '~/models/propertyDefinitionsModel' +import { AvailableFeature, Breadcrumb, Definition, PropertyDefinition } from '~/types' + +import { DataManagementTab } from '../DataManagementScene' import { eventDefinitionsTableLogic } from '../events/eventDefinitionsTableLogic' import { propertyDefinitionsTableLogic } from '../properties/propertyDefinitionsTableLogic' -import { Scene } from 'scenes/sceneTypes' -import { DataManagementTab } from '../DataManagementScene' +import type { definitionLogicType } from './definitionLogicType' export enum DefinitionPageMode { View = 'view', diff --git a/frontend/src/scenes/data-management/events/DefinitionHeader.tsx b/frontend/src/scenes/data-management/events/DefinitionHeader.tsx index 8edb6f208faab..cab89632b62f1 100644 --- a/frontend/src/scenes/data-management/events/DefinitionHeader.tsx +++ b/frontend/src/scenes/data-management/events/DefinitionHeader.tsx @@ -1,16 +1,17 @@ -import { EventDefinition, PropertyDefinition } from '~/types' -import { IconSelectAll } from 'lib/lemon-ui/icons' import { IconBadge, IconBolt, IconCursor, IconEye, IconLeave, IconList, IconLogomark } from '@posthog/icons' -import { getKeyMapping, KEY_MAPPING } from 'lib/taxonomy' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { TaxonomicFilterGroup, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' import { eventTaxonomicGroupProps, propertyTaxonomicGroupProps, } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic' +import { TaxonomicFilterGroup, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { IconSelectAll } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { getKeyMapping, KEY_MAPPING } from 'lib/taxonomy' +import { urls } from 'scenes/urls' + +import { EventDefinition, PropertyDefinition } from '~/types' export function getPropertyDefinitionIcon(definition: PropertyDefinition): JSX.Element { if (KEY_MAPPING.event[definition.name]) { diff --git a/frontend/src/scenes/data-management/events/EventDefinitionProperties.tsx b/frontend/src/scenes/data-management/events/EventDefinitionProperties.tsx index 4d1134eb60133..109afbe5a2c78 100644 --- a/frontend/src/scenes/data-management/events/EventDefinitionProperties.tsx +++ b/frontend/src/scenes/data-management/events/EventDefinitionProperties.tsx @@ -1,12 +1,13 @@ import { useActions, useValues } from 'kea' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { useEffect } from 'react' +import { PropertyDefinitionHeader } from 'scenes/data-management/events/DefinitionHeader' import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' -import { EventDefinition, PropertyDefinition } from '~/types' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' import { organizationLogic } from 'scenes/organizationLogic' -import { PropertyDefinitionHeader } from 'scenes/data-management/events/DefinitionHeader' -import { PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' + +import { EventDefinition, PropertyDefinition } from '~/types' export function EventDefinitionProperties({ definition }: { definition: EventDefinition }): JSX.Element { const { loadPropertiesForEvent } = useActions(eventDefinitionsTableLogic) diff --git a/frontend/src/scenes/data-management/events/EventDefinitionsTable.tsx b/frontend/src/scenes/data-management/events/EventDefinitionsTable.tsx index 54a3bd16b3086..4b89be432b41a 100644 --- a/frontend/src/scenes/data-management/events/EventDefinitionsTable.tsx +++ b/frontend/src/scenes/data-management/events/EventDefinitionsTable.tsx @@ -1,20 +1,22 @@ import './EventDefinitionsTable.scss' + +import { LemonButton, LemonInput, LemonSelect, LemonSelectOptions, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { EventDefinition, EventDefinitionType } from '~/types' -import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' +import { combineUrl } from 'kea-router' import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { organizationLogic } from 'scenes/organizationLogic' +import { TZLabel } from 'lib/components/TZLabel' +import { EVENT_DEFINITIONS_PER_PAGE } from 'lib/constants' +import { IconPlayCircle } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { EventDefinitionHeader } from 'scenes/data-management/events/DefinitionHeader' import { EventDefinitionProperties } from 'scenes/data-management/events/EventDefinitionProperties' -import { LemonButton, LemonInput, LemonSelect, LemonSelectOptions, Link } from '@posthog/lemon-ui' -import { More } from 'lib/lemon-ui/LemonButton/More' +import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' +import { organizationLogic } from 'scenes/organizationLogic' import { urls } from 'scenes/urls' -import { combineUrl } from 'kea-router' -import { IconPlayCircle } from 'lib/lemon-ui/icons' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { TZLabel } from 'lib/components/TZLabel' -import { EVENT_DEFINITIONS_PER_PAGE } from 'lib/constants' + +import { EventDefinition, EventDefinitionType } from '~/types' const eventTypeOptions: LemonSelectOptions = [ { value: EventDefinitionType.Event, label: 'All events', 'data-attr': 'event-type-option-event' }, diff --git a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts index d4b4e66cfe05b..edceacb2d38d4 100644 --- a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts +++ b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.test.ts @@ -1,15 +1,16 @@ -import { initKeaTests } from '~/test/init' -import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' -import { api, MOCK_TEAM_ID } from 'lib/api.mock' -import { expectLogic, partial } from 'kea-test-utils' -import { mockEvent, mockEventDefinitions, mockEventPropertyDefinitions } from '~/test/mocks' -import { useMocks } from '~/mocks/jest' -import { organizationLogic } from 'scenes/organizationLogic' import { combineUrl, router } from 'kea-router' +import { expectLogic, partial } from 'kea-test-utils' +import { api, MOCK_TEAM_ID } from 'lib/api.mock' +import { EVENT_DEFINITIONS_PER_PAGE, PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' import { keyMappingKeys } from 'lib/taxonomy' +import { eventDefinitionsTableLogic } from 'scenes/data-management/events/eventDefinitionsTableLogic' +import { organizationLogic } from 'scenes/organizationLogic' import { urls } from 'scenes/urls' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { mockEvent, mockEventDefinitions, mockEventPropertyDefinitions } from '~/test/mocks' import { EventDefinitionType } from '~/types' -import { EVENT_DEFINITIONS_PER_PAGE, PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' describe('eventDefinitionsTableLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.ts b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.ts index ea35ce72e4dfe..4c7fd59120ae3 100644 --- a/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.ts +++ b/frontend/src/scenes/data-management/events/eventDefinitionsTableLogic.ts @@ -1,14 +1,16 @@ import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { AnyPropertyFilter, EventDefinitionType, EventDefinition, PropertyDefinition } from '~/types' -import type { eventDefinitionsTableLogicType } from './eventDefinitionsTableLogicType' +import { loaders } from 'kea-loaders' +import { actionToUrl, combineUrl, router, urlToAction } from 'kea-router' import api, { PaginatedResponse } from 'lib/api' +import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' +import { EVENT_DEFINITIONS_PER_PAGE, PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' import { keyMappingKeys } from 'lib/taxonomy' -import { actionToUrl, combineUrl, router, urlToAction } from 'kea-router' import { objectsEqual } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { loaders } from 'kea-loaders' -import { EVENT_DEFINITIONS_PER_PAGE, PROPERTY_DEFINITIONS_PER_EVENT } from 'lib/constants' -import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' + +import { AnyPropertyFilter, EventDefinition, EventDefinitionType, PropertyDefinition } from '~/types' + +import type { eventDefinitionsTableLogicType } from './eventDefinitionsTableLogicType' export interface EventDefinitionsPaginatedResponse extends PaginatedResponse { current?: string diff --git a/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx b/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx index 1150d65f52f36..9f7d337b8db58 100644 --- a/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx +++ b/frontend/src/scenes/data-management/ingestion-warnings/IngestionWarningsView.tsx @@ -1,13 +1,15 @@ import { useValues } from 'kea' -import { urls } from 'scenes/urls' -import { IngestionWarning, ingestionWarningsLogic, IngestionWarningSummary } from './ingestionWarningsLogic' -import { LemonTable } from 'lib/lemon-ui/LemonTable' +import { ReadingHog } from 'lib/components/hedgehogs' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' import { TZLabel } from 'lib/components/TZLabel' -import { Link } from 'lib/lemon-ui/Link' +import { LemonTable } from 'lib/lemon-ui/LemonTable' import { TableCellSparkline } from 'lib/lemon-ui/LemonTable/TableCellSparkline' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { Link } from 'lib/lemon-ui/Link' +import { urls } from 'scenes/urls' + import { ProductKey } from '~/types' -import { ReadingHog } from 'lib/components/hedgehogs' + +import { IngestionWarning, ingestionWarningsLogic, IngestionWarningSummary } from './ingestionWarningsLogic' const WARNING_TYPE_TO_DESCRIPTION = { cannot_merge_already_identified: 'Refused to merge an already identified user', diff --git a/frontend/src/scenes/data-management/ingestion-warnings/ingestionWarningsLogic.ts b/frontend/src/scenes/data-management/ingestion-warnings/ingestionWarningsLogic.ts index c5ff0132e7a13..587305ff6d482 100644 --- a/frontend/src/scenes/data-management/ingestion-warnings/ingestionWarningsLogic.ts +++ b/frontend/src/scenes/data-management/ingestion-warnings/ingestionWarningsLogic.ts @@ -1,15 +1,16 @@ -import { kea, connect, path, selectors, afterMount } from 'kea' +import { afterMount, connect, kea, path, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { Breadcrumb } from '~/types' -import { urls } from 'scenes/urls' import api from 'lib/api' - -import type { ingestionWarningsLogicType } from './ingestionWarningsLogicType' -import { teamLogic } from '../../teamLogic' -import { range } from 'lib/utils' import { dayjs, dayjsUtcToTimezone } from 'lib/dayjs' +import { range } from 'lib/utils' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb } from '~/types' + +import { teamLogic } from '../../teamLogic' import { DataManagementTab } from '../DataManagementScene' +import type { ingestionWarningsLogicType } from './ingestionWarningsLogicType' export interface IngestionWarningSummary { type: string diff --git a/frontend/src/scenes/data-management/properties/PropertyDefinitionsTable.tsx b/frontend/src/scenes/data-management/properties/PropertyDefinitionsTable.tsx index aa9de92bec92d..74396c36e63f7 100644 --- a/frontend/src/scenes/data-management/properties/PropertyDefinitionsTable.tsx +++ b/frontend/src/scenes/data-management/properties/PropertyDefinitionsTable.tsx @@ -1,15 +1,17 @@ import './PropertyDefinitionsTable.scss' + +import { LemonInput, LemonSelect, LemonTag, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { PropertyDefinition } from '~/types' import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { organizationLogic } from 'scenes/organizationLogic' +import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { PropertyDefinitionHeader } from 'scenes/data-management/events/DefinitionHeader' import { propertyDefinitionsTableLogic } from 'scenes/data-management/properties/propertyDefinitionsTableLogic' -import { LemonInput, LemonSelect, LemonTag, Link } from '@posthog/lemon-ui' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { organizationLogic } from 'scenes/organizationLogic' import { urls } from 'scenes/urls' -import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' + +import { PropertyDefinition } from '~/types' export function PropertyDefinitionsTable(): JSX.Element { const { propertyDefinitions, propertyDefinitionsLoading, filters, propertyTypeOptions } = diff --git a/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.test.ts b/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.test.ts index 8d9258bb18820..1e50b74a467b3 100644 --- a/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.test.ts +++ b/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.test.ts @@ -1,13 +1,14 @@ -import { initKeaTests } from '~/test/init' -import { api, MOCK_GROUP_TYPES, MOCK_TEAM_ID } from 'lib/api.mock' -import { expectLogic, partial } from 'kea-test-utils' -import { mockEventPropertyDefinitions } from '~/test/mocks' -import { useMocks } from '~/mocks/jest' -import { organizationLogic } from 'scenes/organizationLogic' import { combineUrl, router } from 'kea-router' +import { expectLogic, partial } from 'kea-test-utils' +import { api, MOCK_GROUP_TYPES, MOCK_TEAM_ID } from 'lib/api.mock' +import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' import { propertyDefinitionsTableLogic } from 'scenes/data-management/properties/propertyDefinitionsTableLogic' +import { organizationLogic } from 'scenes/organizationLogic' import { urls } from 'scenes/urls' -import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { mockEventPropertyDefinitions } from '~/test/mocks' describe('propertyDefinitionsTableLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.ts b/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.ts index ea8b7a199975f..a9a1541df21f8 100644 --- a/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.ts +++ b/frontend/src/scenes/data-management/properties/propertyDefinitionsTableLogic.ts @@ -1,20 +1,21 @@ import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { PropertyDefinition } from '~/types' -import api from 'lib/api' +import { loaders } from 'kea-loaders' import { actionToUrl, combineUrl, router, urlToAction } from 'kea-router' +import api from 'lib/api' +import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' +import { LemonSelectOption } from 'lib/lemon-ui/LemonSelect' +import { capitalizeFirstLetter, objectsEqual } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { normalizePropertyDefinitionEndpointUrl, PropertyDefinitionsPaginatedResponse, } from 'scenes/data-management/events/eventDefinitionsTableLogic' -import { capitalizeFirstLetter, objectsEqual } from 'lib/utils' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { loaders } from 'kea-loaders' import { urls } from 'scenes/urls' -import type { propertyDefinitionsTableLogicType } from './propertyDefinitionsTableLogicType' +import { PropertyDefinition } from '~/types' + import { groupsModel } from '../../../models/groupsModel' -import { LemonSelectOption } from 'lib/lemon-ui/LemonSelect' -import { EVENT_PROPERTY_DEFINITIONS_PER_PAGE } from 'lib/constants' +import type { propertyDefinitionsTableLogicType } from './propertyDefinitionsTableLogicType' export interface Filters { property: string diff --git a/frontend/src/scenes/data-warehouse/DataWarehousePageTabs.tsx b/frontend/src/scenes/data-warehouse/DataWarehousePageTabs.tsx index 06aaec8915831..cba833ea548ab 100644 --- a/frontend/src/scenes/data-warehouse/DataWarehousePageTabs.tsx +++ b/frontend/src/scenes/data-warehouse/DataWarehousePageTabs.tsx @@ -1,11 +1,11 @@ +import { actions, kea, path, reducers, useActions, useValues } from 'kea' import { actionToUrl, urlToAction } from 'kea-router' -import { kea, useActions, useValues, path, actions, reducers } from 'kea' -import { urls } from 'scenes/urls' +import { FEATURE_FLAGS } from 'lib/constants' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { urls } from 'scenes/urls' import type { dataWarehouseTabsLogicType } from './DataWarehousePageTabsType' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' export enum DataWarehouseTab { Posthog = 'posthog', diff --git a/frontend/src/scenes/data-warehouse/ViewLinkModal.tsx b/frontend/src/scenes/data-warehouse/ViewLinkModal.tsx index 9d6c253a1cb78..291eaf6836d6e 100644 --- a/frontend/src/scenes/data-warehouse/ViewLinkModal.tsx +++ b/frontend/src/scenes/data-warehouse/ViewLinkModal.tsx @@ -1,10 +1,11 @@ import './ViewLinkModal.scss' import { LemonButton, LemonDivider, LemonModal, LemonSelect, LemonTag } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { Field, Form } from 'kea-forms' import { IconDelete, IconSwapHoriz } from 'lib/lemon-ui/icons' import { viewLinkLogic } from 'scenes/data-warehouse/viewLinkLogic' -import { Form, Field } from 'kea-forms' -import { useActions, useValues } from 'kea' + import { DatabaseSchemaQueryResponseField } from '~/queries/schema' export function ViewLinkModal({ tableSelectable }: { tableSelectable: boolean }): JSX.Element { diff --git a/frontend/src/scenes/data-warehouse/external/DataWarehouseExternalScene.tsx b/frontend/src/scenes/data-warehouse/external/DataWarehouseExternalScene.tsx index 4f7133fff9e4e..fd5889be6fd6d 100644 --- a/frontend/src/scenes/data-warehouse/external/DataWarehouseExternalScene.tsx +++ b/frontend/src/scenes/data-warehouse/external/DataWarehouseExternalScene.tsx @@ -1,18 +1,20 @@ -import { LemonTag, Link, LemonButtonWithSideAction, LemonButton } from '@posthog/lemon-ui' -import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' -import { urls } from 'scenes/urls' +import { LemonButton, LemonButtonWithSideAction, LemonTag, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { router } from 'kea-router' +import { PageHeader } from 'lib/components/PageHeader' import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { FEATURE_FLAGS } from 'lib/constants' +import { IconSettings } from 'lib/lemon-ui/icons' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { ProductKey } from '~/types' -import { DataWarehouseTablesContainer } from './DataWarehouseTables' -import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' + import { DataWarehousePageTabs, DataWarehouseTab } from '../DataWarehousePageTabs' +import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' +import { DataWarehouseTablesContainer } from './DataWarehouseTables' import SourceModal from './SourceModal' -import { IconSettings } from 'lib/lemon-ui/icons' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' export const scene: SceneExport = { component: DataWarehouseExternalScene, diff --git a/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx b/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx index 11d16e85e8d3f..35b3270e71516 100644 --- a/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx +++ b/frontend/src/scenes/data-warehouse/external/DataWarehouseTables.tsx @@ -1,12 +1,13 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { DatabaseTables } from 'scenes/data-management/database/DatabaseTables' -import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' -import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable' import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonButton } from '@posthog/lemon-ui' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable' +import { DatabaseTables } from 'scenes/data-management/database/DatabaseTables' import { teamLogic } from 'scenes/teamLogic' + import { DataWarehouseSceneRow } from '../types' +import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' export function DataWarehouseTablesContainer(): JSX.Element { const { tables, dataWarehouseLoading } = useValues(dataWarehouseSceneLogic) diff --git a/frontend/src/scenes/data-warehouse/external/SourceModal.tsx b/frontend/src/scenes/data-warehouse/external/SourceModal.tsx index 2308ec31b3140..4b8a98e04cc29 100644 --- a/frontend/src/scenes/data-warehouse/external/SourceModal.tsx +++ b/frontend/src/scenes/data-warehouse/external/SourceModal.tsx @@ -1,11 +1,12 @@ import { LemonButton, LemonDivider, LemonInput, LemonModal, LemonModalProps } from '@posthog/lemon-ui' -import { Form } from 'kea-forms' -import { ConnectorConfigType, sourceModalLogic } from './sourceModalLogic' import { useActions, useValues } from 'kea' -import { DatawarehouseTableForm } from '../new_table/DataWarehouseTableForm' +import { Form } from 'kea-forms' import { Field } from 'lib/forms/Field' import stripeLogo from 'public/stripe-logo.svg' +import { DatawarehouseTableForm } from '../new_table/DataWarehouseTableForm' +import { ConnectorConfigType, sourceModalLogic } from './sourceModalLogic' + interface SourceModalProps extends LemonModalProps {} export default function SourceModal(props: SourceModalProps): JSX.Element { diff --git a/frontend/src/scenes/data-warehouse/external/dataWarehouseSceneLogic.tsx b/frontend/src/scenes/data-warehouse/external/dataWarehouseSceneLogic.tsx index 238945ac9f5b9..13ebc2dda9f32 100644 --- a/frontend/src/scenes/data-warehouse/external/dataWarehouseSceneLogic.tsx +++ b/frontend/src/scenes/data-warehouse/external/dataWarehouseSceneLogic.tsx @@ -1,11 +1,12 @@ import { actions, afterMount, connect, kea, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api, { PaginatedResponse } from 'lib/api' -import { DataWarehouseTable, ProductKey } from '~/types' import { userLogic } from 'scenes/userLogic' -import type { dataWarehouseSceneLogicType } from './dataWarehouseSceneLogicType' +import { DataWarehouseTable, ProductKey } from '~/types' + import { DataWarehouseSceneRow } from '../types' +import type { dataWarehouseSceneLogicType } from './dataWarehouseSceneLogicType' export const dataWarehouseSceneLogic = kea([ path(['scenes', 'warehouse', 'dataWarehouseSceneLogic']), diff --git a/frontend/src/scenes/data-warehouse/external/sourceModalLogic.ts b/frontend/src/scenes/data-warehouse/external/sourceModalLogic.ts index 398db7f8247a3..2f616fe91c1af 100644 --- a/frontend/src/scenes/data-warehouse/external/sourceModalLogic.ts +++ b/frontend/src/scenes/data-warehouse/external/sourceModalLogic.ts @@ -1,15 +1,16 @@ -import { actions, connect, kea, path, reducers, selectors, listeners } from 'kea' - -import type { sourceModalLogicType } from './sourceModalLogicType' -import { forms } from 'kea-forms' -import { ExternalDataStripeSourceCreatePayload } from '~/types' -import api from 'lib/api' import { lemonToast } from '@posthog/lemon-ui' -import { dataWarehouseTableLogic } from '../new_table/dataWarehouseTableLogic' -import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { forms } from 'kea-forms' import { router } from 'kea-router' +import api from 'lib/api' import { urls } from 'scenes/urls' + +import { ExternalDataStripeSourceCreatePayload } from '~/types' + +import { dataWarehouseTableLogic } from '../new_table/dataWarehouseTableLogic' import { dataWarehouseSettingsLogic } from '../settings/dataWarehouseSettingsLogic' +import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' +import type { sourceModalLogicType } from './sourceModalLogicType' export interface ConnectorConfigType { name: string diff --git a/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableForm.tsx b/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableForm.tsx index 25f593df020db..ae6d5120d8316 100644 --- a/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableForm.tsx +++ b/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableForm.tsx @@ -1,8 +1,9 @@ -import { dataWarehouseTableLogic } from './dataWarehouseTableLogic' -import { Form } from 'kea-forms' import { LemonInput, LemonSelect } from '@posthog/lemon-ui' +import { Form } from 'kea-forms' import { Field } from 'lib/forms/Field' +import { dataWarehouseTableLogic } from './dataWarehouseTableLogic' + interface DataWarehouseTableFormProps { footer?: JSX.Element } diff --git a/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableScene.tsx b/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableScene.tsx index e7c2fbf15e94a..f547d46cd7d1e 100644 --- a/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableScene.tsx +++ b/frontend/src/scenes/data-warehouse/new_table/DataWarehouseTableScene.tsx @@ -1,11 +1,12 @@ -import { dataWarehouseTableLogic } from './dataWarehouseTableLogic' import { LemonButton, Link } from '@posthog/lemon-ui' -import { SceneExport } from 'scenes/sceneTypes' -import { PageHeader } from 'lib/components/PageHeader' import { useActions, useValues } from 'kea' import { router } from 'kea-router' +import { PageHeader } from 'lib/components/PageHeader' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + import { DatawarehouseTableForm } from './DataWarehouseTableForm' +import { dataWarehouseTableLogic } from './dataWarehouseTableLogic' export const scene: SceneExport = { component: DataWarehouseTable, diff --git a/frontend/src/scenes/data-warehouse/new_table/dataWarehouseTableLogic.tsx b/frontend/src/scenes/data-warehouse/new_table/dataWarehouseTableLogic.tsx index 7ca0685b35192..88ab7dfa92962 100644 --- a/frontend/src/scenes/data-warehouse/new_table/dataWarehouseTableLogic.tsx +++ b/frontend/src/scenes/data-warehouse/new_table/dataWarehouseTableLogic.tsx @@ -1,16 +1,18 @@ import { lemonToast } from '@posthog/lemon-ui' -import { kea, path, props, listeners, reducers, actions, selectors, connect } from 'kea' +import { actions, connect, kea, listeners, path, props, reducers, selectors } from 'kea' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { router } from 'kea-router' import api from 'lib/api' +import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic' +import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { AnyPropertyFilter, Breadcrumb, DataWarehouseTable } from '~/types' + import { DataTableNode } from '~/queries/schema' -import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic' -import type { dataWarehouseTableLogicType } from './dataWarehouseTableLogicType' +import { AnyPropertyFilter, Breadcrumb, DataWarehouseTable } from '~/types' + import { dataWarehouseSceneLogic } from '../external/dataWarehouseSceneLogic' -import { Scene } from 'scenes/sceneTypes' +import type { dataWarehouseTableLogicType } from './dataWarehouseTableLogicType' export interface TableLogicProps { /** A UUID or 'new'. */ diff --git a/frontend/src/scenes/data-warehouse/posthog/DataWarehousePosthogScene.tsx b/frontend/src/scenes/data-warehouse/posthog/DataWarehousePosthogScene.tsx index 6e91637178af1..8d181bf41b915 100644 --- a/frontend/src/scenes/data-warehouse/posthog/DataWarehousePosthogScene.tsx +++ b/frontend/src/scenes/data-warehouse/posthog/DataWarehousePosthogScene.tsx @@ -1,14 +1,15 @@ import { LemonButton, LemonTag, Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic' -import { DataWarehousePageTabs, DataWarehouseTab } from '../DataWarehousePageTabs' import { DatabaseTablesContainer } from 'scenes/data-management/database/DatabaseTables' -import { ViewLinkModal } from '../ViewLinkModal' -import { useActions, useValues } from 'kea' +import { SceneExport } from 'scenes/sceneTypes' + +import { DataWarehousePageTabs, DataWarehouseTab } from '../DataWarehousePageTabs' import { viewLinkLogic } from '../viewLinkLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' +import { ViewLinkModal } from '../ViewLinkModal' export const scene: SceneExport = { component: DataWarehousePosthogScene, diff --git a/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesContainer.tsx b/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesContainer.tsx index 25f506a4df903..75141f82ec1c1 100644 --- a/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesContainer.tsx +++ b/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesContainer.tsx @@ -1,15 +1,17 @@ +import { LemonButton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { DatabaseTables } from 'scenes/data-management/database/DatabaseTables' -import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable' +import { router } from 'kea-router' import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonButton, Link } from '@posthog/lemon-ui' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { DatabaseTable } from 'scenes/data-management/database/DatabaseTable' +import { DatabaseTables } from 'scenes/data-management/database/DatabaseTables' import { teamLogic } from 'scenes/teamLogic' -import { DataWarehouseSceneRow } from '../types' -import { dataWarehouseSavedQueriesLogic } from './dataWarehouseSavedQueriesLogic' import { urls } from 'scenes/urls' + import { DataTableNode, HogQLQuery, NodeKind } from '~/queries/schema' -import { router } from 'kea-router' + +import { DataWarehouseSceneRow } from '../types' +import { dataWarehouseSavedQueriesLogic } from './dataWarehouseSavedQueriesLogic' export function DataWarehouseSavedQueriesContainer(): JSX.Element { const { savedQueries, dataWarehouseSavedQueriesLoading } = useValues(dataWarehouseSavedQueriesLogic) diff --git a/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesScene.tsx b/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesScene.tsx index eb78288c85efb..0b630fd12efa8 100644 --- a/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesScene.tsx +++ b/frontend/src/scenes/data-warehouse/saved_queries/DataWarehouseSavedQueriesScene.tsx @@ -1,17 +1,19 @@ import { LemonButton, LemonTag, Link } from '@posthog/lemon-ui' +import { useValues } from 'kea' +import { router } from 'kea-router' import { PageHeader } from 'lib/components/PageHeader' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + +import { Error404 } from '~/layout/Error404' +import { ProductKey } from '~/types' + import { DataWarehousePageTabs, DataWarehouseTab } from '../DataWarehousePageTabs' -import { dataWarehouseSavedQueriesLogic } from './dataWarehouseSavedQueriesLogic' import { DataWarehouseSavedQueriesContainer } from './DataWarehouseSavedQueriesContainer' -import { useValues } from 'kea' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' -import { router } from 'kea-router' -import { ProductKey } from '~/types' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { Error404 } from '~/layout/Error404' +import { dataWarehouseSavedQueriesLogic } from './dataWarehouseSavedQueriesLogic' export const scene: SceneExport = { component: DataWarehouseSavedQueriesScene, diff --git a/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseSavedQueriesLogic.tsx b/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseSavedQueriesLogic.tsx index f8776ffbd0e7b..1fe221605f0c0 100644 --- a/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseSavedQueriesLogic.tsx +++ b/frontend/src/scenes/data-warehouse/saved_queries/dataWarehouseSavedQueriesLogic.tsx @@ -1,14 +1,14 @@ import { afterMount, connect, kea, listeners, path, selectors } from 'kea' import { loaders } from 'kea-loaders' +import { router } from 'kea-router' import api, { PaginatedResponse } from 'lib/api' -import { DataWarehouseSavedQuery, ProductKey } from '~/types' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { DataWarehouseSceneRow } from '../types' +import { DataWarehouseSavedQuery, ProductKey } from '~/types' +import { DataWarehouseSceneRow } from '../types' import type { dataWarehouseSavedQueriesLogicType } from './dataWarehouseSavedQueriesLogicType' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' export const dataWarehouseSavedQueriesLogic = kea([ path(['scenes', 'warehouse', 'dataWarehouseSavedQueriesLogic']), diff --git a/frontend/src/scenes/data-warehouse/settings/DataWarehouseSettingsScene.tsx b/frontend/src/scenes/data-warehouse/settings/DataWarehouseSettingsScene.tsx index eb158c2f97c52..45f8888958773 100644 --- a/frontend/src/scenes/data-warehouse/settings/DataWarehouseSettingsScene.tsx +++ b/frontend/src/scenes/data-warehouse/settings/DataWarehouseSettingsScene.tsx @@ -1,13 +1,14 @@ import { LemonButton, LemonTable, LemonTag, Spinner } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' +import { FEATURE_FLAGS } from 'lib/constants' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { SceneExport } from 'scenes/sceneTypes' -import { dataWarehouseSettingsLogic } from './dataWarehouseSettingsLogic' -import { useActions, useValues } from 'kea' + import { dataWarehouseSceneLogic } from '../external/dataWarehouseSceneLogic' import SourceModal from '../external/SourceModal' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { More } from 'lib/lemon-ui/LemonButton/More' +import { dataWarehouseSettingsLogic } from './dataWarehouseSettingsLogic' export const scene: SceneExport = { component: DataWarehouseSettingsScene, diff --git a/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts b/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts index 2941effa9d151..00c4db0b08f30 100644 --- a/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts +++ b/frontend/src/scenes/data-warehouse/settings/dataWarehouseSettingsLogic.ts @@ -1,11 +1,12 @@ import { actions, afterMount, kea, listeners, path, reducers, selectors } from 'kea' - -import type { dataWarehouseSettingsLogicType } from './dataWarehouseSettingsLogicType' import { loaders } from 'kea-loaders' import api, { PaginatedResponse } from 'lib/api' -import { ExternalDataStripeSource, Breadcrumb } from '~/types' -import { urls } from 'scenes/urls' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb, ExternalDataStripeSource } from '~/types' + +import type { dataWarehouseSettingsLogicType } from './dataWarehouseSettingsLogicType' export interface DataWarehouseSource {} diff --git a/frontend/src/scenes/data-warehouse/viewLinkLogic.tsx b/frontend/src/scenes/data-warehouse/viewLinkLogic.tsx index 9e867642eed89..c8f4bafc4999d 100644 --- a/frontend/src/scenes/data-warehouse/viewLinkLogic.tsx +++ b/frontend/src/scenes/data-warehouse/viewLinkLogic.tsx @@ -1,12 +1,14 @@ -import { actions, connect, kea, selectors, listeners, reducers, path, afterMount } from 'kea' -import { dataWarehouseSavedQueriesLogic } from './saved_queries/dataWarehouseSavedQueriesLogic' -import { DataWarehouseSceneRow } from './types' -import { DataWarehouseViewLink } from '~/types' +import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { forms } from 'kea-forms' -import api from 'lib/api' -import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic' import { loaders } from 'kea-loaders' +import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { databaseTableListLogic } from 'scenes/data-management/database/databaseTableListLogic' + +import { DataWarehouseViewLink } from '~/types' + +import { dataWarehouseSavedQueriesLogic } from './saved_queries/dataWarehouseSavedQueriesLogic' +import { DataWarehouseSceneRow } from './types' import type { viewLinkLogicType } from './viewLinkLogicType' import { ViewLinkKeyLabel } from './ViewLinkModal' diff --git a/frontend/src/scenes/debug/DebugScene.tsx b/frontend/src/scenes/debug/DebugScene.tsx index 19cba38cb9d5e..018f72067e2e2 100644 --- a/frontend/src/scenes/debug/DebugScene.tsx +++ b/frontend/src/scenes/debug/DebugScene.tsx @@ -1,14 +1,16 @@ -import { debugSceneLogic } from './debugSceneLogic' -import { SceneExport } from 'scenes/sceneTypes' -import { PageHeader } from 'lib/components/PageHeader' -import { Query } from '~/queries/Query/Query' import { useActions, useValues } from 'kea' -import { stringifiedExamples } from '~/queries/examples' -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { PageHeader } from 'lib/components/PageHeader' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { HogQLQuery } from '~/queries/schema' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' import { HogQLDebug } from 'scenes/debug/HogQLDebug' +import { SceneExport } from 'scenes/sceneTypes' + +import { stringifiedExamples } from '~/queries/examples' +import { Query } from '~/queries/Query/Query' +import { HogQLQuery } from '~/queries/schema' + +import { debugSceneLogic } from './debugSceneLogic' interface QueryDebugProps { queryKey: string diff --git a/frontend/src/scenes/debug/HogQLDebug.tsx b/frontend/src/scenes/debug/HogQLDebug.tsx index 4ef2a6656bede..c32a4328cb3b8 100644 --- a/frontend/src/scenes/debug/HogQLDebug.tsx +++ b/frontend/src/scenes/debug/HogQLDebug.tsx @@ -1,15 +1,16 @@ -import { HogQLQueryEditor } from '~/queries/nodes/HogQLQuery/HogQLQueryEditor' -import { DataNode, HogQLQuery, HogQLQueryResponse } from '~/queries/schema' -import { DateRange } from '~/queries/nodes/DataNode/DateRange' -import { EventPropertyFilters } from '~/queries/nodes/EventsNode/EventPropertyFilters' import { BindLogic, useValues } from 'kea' -import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' -import { ElapsedTime, Timings } from '~/queries/nodes/DataNode/ElapsedTime' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { CodeEditor } from 'lib/components/CodeEditors' -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { LemonLabel } from 'lib/lemon-ui/LemonLabel' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' + +import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' +import { DateRange } from '~/queries/nodes/DataNode/DateRange' +import { ElapsedTime, Timings } from '~/queries/nodes/DataNode/ElapsedTime' import { Reload } from '~/queries/nodes/DataNode/Reload' +import { EventPropertyFilters } from '~/queries/nodes/EventsNode/EventPropertyFilters' +import { HogQLQueryEditor } from '~/queries/nodes/HogQLQuery/HogQLQueryEditor' +import { DataNode, HogQLQuery, HogQLQueryResponse } from '~/queries/schema' interface HogQLDebugProps { queryKey: string diff --git a/frontend/src/scenes/debug/debugSceneLogic.ts b/frontend/src/scenes/debug/debugSceneLogic.ts index 912474eac6907..4191584f69402 100644 --- a/frontend/src/scenes/debug/debugSceneLogic.ts +++ b/frontend/src/scenes/debug/debugSceneLogic.ts @@ -1,10 +1,11 @@ import { actions, kea, path, reducers } from 'kea' - -import type { debugSceneLogicType } from './debugSceneLogicType' import { actionToUrl, urlToAction } from 'kea-router' import { urls } from 'scenes/urls' + import { stringifiedExamples } from '~/queries/examples' +import type { debugSceneLogicType } from './debugSceneLogicType' + const DEFAULT_QUERY: string = stringifiedExamples['HogQLRaw'] export const debugSceneLogic = kea([ diff --git a/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx b/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx index 959d57825aae5..adfaae1895f33 100644 --- a/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx +++ b/frontend/src/scenes/early-access-features/EarlyAccessFeature.tsx @@ -1,10 +1,24 @@ import { LemonButton, LemonDivider, LemonInput, LemonSkeleton, LemonTag, LemonTextArea, Link } from '@posthog/lemon-ui' +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' +import { Form } from 'kea-forms' +import { router } from 'kea-router' +import { FlagSelector } from 'lib/components/FlagSelector' +import { NotFound } from 'lib/components/NotFound' import { PageHeader } from 'lib/components/PageHeader' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { Field, PureField } from 'lib/forms/Field' +import { IconClose, IconFlag, IconHelpOutline } from 'lib/lemon-ui/icons' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' +import { personsLogic, PersonsLogicProps } from 'scenes/persons/personsLogic' +import { PersonsSearch } from 'scenes/persons/PersonsSearch' +import { PersonsTable } from 'scenes/persons/PersonsTable' import { SceneExport } from 'scenes/sceneTypes' -import { earlyAccessFeatureLogic } from './earlyAccessFeatureLogic' -import { Form } from 'kea-forms' +import { urls } from 'scenes/urls' + import { EarlyAccessFeatureStage, EarlyAccessFeatureTabs, @@ -13,21 +27,9 @@ import { PropertyFilterType, PropertyOperator, } from '~/types' -import { urls } from 'scenes/urls' -import { IconClose, IconFlag, IconHelpOutline } from 'lib/lemon-ui/icons' -import { router } from 'kea-router' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' -import { personsLogic, PersonsLogicProps } from 'scenes/persons/personsLogic' -import clsx from 'clsx' + +import { earlyAccessFeatureLogic } from './earlyAccessFeatureLogic' import { InstructionsModal } from './InstructionsModal' -import { PersonsTable } from 'scenes/persons/PersonsTable' -import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { PersonsSearch } from 'scenes/persons/PersonsSearch' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { NotFound } from 'lib/components/NotFound' -import { FlagSelector } from 'lib/components/FlagSelector' export const scene: SceneExport = { component: EarlyAccessFeature, diff --git a/frontend/src/scenes/early-access-features/EarlyAccessFeatures.stories.tsx b/frontend/src/scenes/early-access-features/EarlyAccessFeatures.stories.tsx index b340f584e6cce..81fb329a5f72d 100644 --- a/frontend/src/scenes/early-access-features/EarlyAccessFeatures.stories.tsx +++ b/frontend/src/scenes/early-access-features/EarlyAccessFeatures.stories.tsx @@ -1,8 +1,9 @@ -import { useEffect } from 'react' import { Meta } from '@storybook/react' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { useEffect } from 'react' import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' import { EarlyAccessFeatureType } from '~/types' diff --git a/frontend/src/scenes/early-access-features/EarlyAccessFeatures.tsx b/frontend/src/scenes/early-access-features/EarlyAccessFeatures.tsx index 594a986400042..79b03f9da0e5a 100644 --- a/frontend/src/scenes/early-access-features/EarlyAccessFeatures.tsx +++ b/frontend/src/scenes/early-access-features/EarlyAccessFeatures.tsx @@ -1,13 +1,15 @@ import { LemonButton, LemonTable, LemonTag, Link } from '@posthog/lemon-ui' import { useValues } from 'kea' +import { router } from 'kea-router' import { PageHeader } from 'lib/components/PageHeader' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + import { EarlyAccessFeatureType, ProductKey } from '~/types' + import { earlyAccessFeaturesLogic } from './earlyAccessFeaturesLogic' -import { userLogic } from 'scenes/userLogic' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' -import { router } from 'kea-router' export const scene: SceneExport = { component: EarlyAccessFeatures, diff --git a/frontend/src/scenes/early-access-features/InstructionsModal.tsx b/frontend/src/scenes/early-access-features/InstructionsModal.tsx index 1e76f9ba5f06e..d3cda50184d25 100644 --- a/frontend/src/scenes/early-access-features/InstructionsModal.tsx +++ b/frontend/src/scenes/early-access-features/InstructionsModal.tsx @@ -1,9 +1,10 @@ import { LemonCollapse, LemonModal, Link } from '@posthog/lemon-ui' +import { useValues } from 'kea' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { FeatureFlagType } from '~/types' import EarlyAccessFeatureImage from 'public/early-access-feature-demo.png' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { useValues } from 'kea' + +import { FeatureFlagType } from '~/types' interface InstructionsModalProps { featureFlag: FeatureFlagType diff --git a/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts b/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts index 0ea2885e60c3f..cfc21f71a21fe 100644 --- a/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts +++ b/frontend/src/scenes/early-access-features/earlyAccessFeatureLogic.ts @@ -1,9 +1,13 @@ +import { lemonToast } from '@posthog/lemon-ui' import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { router, urlToAction } from 'kea-router' import api from 'lib/api' +import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' + import { Breadcrumb, EarlyAccessFeatureStage, @@ -11,11 +15,9 @@ import { EarlyAccessFeatureType, NewEarlyAccessFeatureType, } from '~/types' + import type { earlyAccessFeatureLogicType } from './earlyAccessFeatureLogicType' import { earlyAccessFeaturesLogic } from './earlyAccessFeaturesLogic' -import { teamLogic } from 'scenes/teamLogic' -import { lemonToast } from '@posthog/lemon-ui' -import { Scene } from 'scenes/sceneTypes' export const NEW_EARLY_ACCESS_FEATURE: NewEarlyAccessFeatureType = { name: '', diff --git a/frontend/src/scenes/early-access-features/earlyAccessFeaturesLogic.ts b/frontend/src/scenes/early-access-features/earlyAccessFeaturesLogic.ts index b7032217494b4..25fc3741bb9f6 100644 --- a/frontend/src/scenes/early-access-features/earlyAccessFeaturesLogic.ts +++ b/frontend/src/scenes/early-access-features/earlyAccessFeaturesLogic.ts @@ -1,11 +1,12 @@ import { afterMount, kea, path, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { Breadcrumb, EarlyAccessFeatureType } from '~/types' import type { earlyAccessFeaturesLogicType } from './earlyAccessFeaturesLogicType' -import { urls } from 'scenes/urls' -import { Scene } from 'scenes/sceneTypes' export const earlyAccessFeaturesLogic = kea([ path(['scenes', 'features', 'featuresLogic']), diff --git a/frontend/src/scenes/events/EventDetails.tsx b/frontend/src/scenes/events/EventDetails.tsx index 9bf9efcf96648..59e5ed057bafe 100644 --- a/frontend/src/scenes/events/EventDetails.tsx +++ b/frontend/src/scenes/events/EventDetails.tsx @@ -1,19 +1,20 @@ -import { useState } from 'react' -import { KEY_MAPPING } from 'lib/taxonomy' -import { PropertiesTable } from 'lib/components/PropertiesTable' -import { HTMLElementsDisplay } from 'lib/components/HTMLElementsDisplay/HTMLElementsDisplay' -import { EventJSON } from 'scenes/events/EventJSON' -import { EventType, PropertyDefinitionType } from '~/types' +import './EventDetails.scss' + +import ReactJson from '@microlink/react-json-view' import { Properties } from '@posthog/plugin-scaffold' +import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay' +import { HTMLElementsDisplay } from 'lib/components/HTMLElementsDisplay/HTMLElementsDisplay' +import { PropertiesTable } from 'lib/components/PropertiesTable' import { dayjs } from 'lib/dayjs' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { pluralize } from 'lib/utils' import { LemonTableProps } from 'lib/lemon-ui/LemonTable' -import ReactJson from '@microlink/react-json-view' -import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { KEY_MAPPING } from 'lib/taxonomy' +import { pluralize } from 'lib/utils' +import { useState } from 'react' +import { EventJSON } from 'scenes/events/EventJSON' -import './EventDetails.scss' +import { EventType, PropertyDefinitionType } from '~/types' interface EventDetailsProps { event: EventType diff --git a/frontend/src/scenes/events/Events.stories.tsx b/frontend/src/scenes/events/Events.stories.tsx index 8ee043b5c500c..f650449e755ab 100644 --- a/frontend/src/scenes/events/Events.stories.tsx +++ b/frontend/src/scenes/events/Events.stories.tsx @@ -1,11 +1,12 @@ import { Meta } from '@storybook/react' - +import { router } from 'kea-router' import { useEffect } from 'react' +import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' + import eventsQuery from './__mocks__/eventsQuery.json' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import { App } from 'scenes/App' const meta: Meta = { title: 'Scenes-App/Events', diff --git a/frontend/src/scenes/events/Events.tsx b/frontend/src/scenes/events/Events.tsx index aa30ee553c381..0a2e94cd634ae 100644 --- a/frontend/src/scenes/events/Events.tsx +++ b/frontend/src/scenes/events/Events.tsx @@ -1,6 +1,6 @@ -import { SceneExport } from 'scenes/sceneTypes' import { PageHeader } from 'lib/components/PageHeader' import { EventsScene } from 'scenes/events/EventsScene' +import { SceneExport } from 'scenes/sceneTypes' export const scene: SceneExport = { component: Events, diff --git a/frontend/src/scenes/events/EventsScene.tsx b/frontend/src/scenes/events/EventsScene.tsx index 6b450242da5a3..d727a2371d3a8 100644 --- a/frontend/src/scenes/events/EventsScene.tsx +++ b/frontend/src/scenes/events/EventsScene.tsx @@ -1,5 +1,6 @@ import { useActions, useValues } from 'kea' import { eventsSceneLogic } from 'scenes/events/eventsSceneLogic' + import { Query } from '~/queries/Query/Query' export function EventsScene(): JSX.Element { diff --git a/frontend/src/scenes/events/Owner.tsx b/frontend/src/scenes/events/Owner.tsx index 600be23efbe12..308912d41d455 100644 --- a/frontend/src/scenes/events/Owner.tsx +++ b/frontend/src/scenes/events/Owner.tsx @@ -1,5 +1,6 @@ import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { CSSProperties } from 'react' + import { UserBasicType } from '~/types' export function Owner({ user, style = {} }: { user?: UserBasicType | null; style?: CSSProperties }): JSX.Element { diff --git a/frontend/src/scenes/events/createActionFromEvent.test.js b/frontend/src/scenes/events/createActionFromEvent.test.js index 3ea1f6a2330c2..e98e446466a17 100644 --- a/frontend/src/scenes/events/createActionFromEvent.test.js +++ b/frontend/src/scenes/events/createActionFromEvent.test.js @@ -1,8 +1,10 @@ -import { api } from 'lib/api.mock' import { router } from 'kea-router' -import { createActionFromEvent } from './createActionFromEvent' +import { api } from 'lib/api.mock' + import { initKeaTests } from '~/test/init' +import { createActionFromEvent } from './createActionFromEvent' + describe('createActionFromEvent()', () => { given( 'subject', diff --git a/frontend/src/scenes/events/createActionFromEvent.tsx b/frontend/src/scenes/events/createActionFromEvent.tsx index 0e0f584ad12d7..d12f365efb3a7 100644 --- a/frontend/src/scenes/events/createActionFromEvent.tsx +++ b/frontend/src/scenes/events/createActionFromEvent.tsx @@ -1,20 +1,21 @@ import { router } from 'kea-router' +import { CLICK_TARGETS, elementToSelector, matchesDataAttribute } from 'lib/actionUtils' import api from 'lib/api' -import { autoCaptureEventToDescription } from 'lib/utils' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { Link } from 'lib/lemon-ui/Link' +import { autoCaptureEventToDescription } from 'lib/utils' +import { urls } from 'scenes/urls' + import { ActionStepType, - StringMatching, ActionType, ElementType, EventType, PropertyFilterType, PropertyOperator, + StringMatching, TeamType, } from '~/types' -import { CLICK_TARGETS, elementToSelector, matchesDataAttribute } from 'lib/actionUtils' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { urls } from 'scenes/urls' export function recurseSelector(elements: ElementType[], parts: string, index: number): string { const element = elements[index] diff --git a/frontend/src/scenes/events/defaults.ts b/frontend/src/scenes/events/defaults.ts index eb62b5ce22c80..97fd86203ec4c 100644 --- a/frontend/src/scenes/events/defaults.ts +++ b/frontend/src/scenes/events/defaults.ts @@ -1,5 +1,5 @@ -import { DataTableNode, NodeKind } from '~/queries/schema' import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' +import { DataTableNode, NodeKind } from '~/queries/schema' import { AnyPropertyFilter } from '~/types' export const getDefaultEventsSceneQuery = (properties?: AnyPropertyFilter[]): DataTableNode => ({ diff --git a/frontend/src/scenes/events/eventsSceneLogic.tsx b/frontend/src/scenes/events/eventsSceneLogic.tsx index 8fff224f4ea5f..08912e5666d76 100644 --- a/frontend/src/scenes/events/eventsSceneLogic.tsx +++ b/frontend/src/scenes/events/eventsSceneLogic.tsx @@ -1,15 +1,16 @@ +import equal from 'fast-deep-equal' import { actions, connect, kea, path, reducers, selectors } from 'kea' - -import type { eventsSceneLogicType } from './eventsSceneLogicType' import { actionToUrl, urlToAction } from 'kea-router' -import equal from 'fast-deep-equal' -import { Node } from '~/queries/schema' -import { urls } from 'scenes/urls' -import { objectsEqual } from 'lib/utils' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { objectsEqual } from 'lib/utils' import { getDefaultEventsSceneQuery } from 'scenes/events/defaults' import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + import { getDefaultEventsQueryForTeam } from '~/queries/nodes/DataTable/defaultEventsQuery' +import { Node } from '~/queries/schema' + +import type { eventsSceneLogicType } from './eventsSceneLogicType' export const eventsSceneLogic = kea([ path(['scenes', 'events', 'eventsSceneLogic']), diff --git a/frontend/src/scenes/experiments/Experiment.stories.tsx b/frontend/src/scenes/experiments/Experiment.stories.tsx index a80c329c6e478..041dab5a4ad78 100644 --- a/frontend/src/scenes/experiments/Experiment.stories.tsx +++ b/frontend/src/scenes/experiments/Experiment.stories.tsx @@ -1,9 +1,11 @@ -import { useEffect } from 'react' import { Meta, StoryFn } from '@storybook/react' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { useEffect } from 'react' import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' +import { useAvailableFeatures } from '~/mocks/features' import { toPaginatedResponse } from '~/mocks/handlers' import { AvailableFeature, @@ -20,7 +22,6 @@ import { SignificanceCode, TrendsExperimentResults, } from '~/types' -import { useAvailableFeatures } from '~/mocks/features' const MOCK_FUNNEL_EXPERIMENT: Experiment = { id: 1, diff --git a/frontend/src/scenes/experiments/Experiment.tsx b/frontend/src/scenes/experiments/Experiment.tsx index 30cc2e011e9c6..641ab21598c20 100644 --- a/frontend/src/scenes/experiments/Experiment.tsx +++ b/frontend/src/scenes/experiments/Experiment.tsx @@ -1,24 +1,5 @@ -import { Popconfirm, Progress } from 'antd' -import { BindLogic, useActions, useValues } from 'kea' -import { PageHeader } from 'lib/components/PageHeader' -import { useEffect, useState } from 'react' -import { insightLogic } from 'scenes/insights/insightLogic' -import { SceneExport } from 'scenes/sceneTypes' -import { AvailableFeature, Experiment as ExperimentType, FunnelStep, InsightType } from '~/types' import './Experiment.scss' -import { experimentLogic, ExperimentLogicProps } from './experimentLogic' -import { IconDelete, IconPlusMini } from 'lib/lemon-ui/icons' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { dayjs } from 'lib/dayjs' -import { capitalizeFirstLetter, humanFriendlyNumber } from 'lib/utils' -import { SecondaryMetrics } from './SecondaryMetrics' -import { EditableField } from 'lib/components/EditableField/EditableField' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' -import { ExperimentPreview } from './ExperimentPreview' -import { ExperimentImplementationDetails } from './ExperimentImplementationDetails' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { router } from 'kea-router' + import { LemonDivider, LemonInput, @@ -29,23 +10,45 @@ import { LemonTextArea, Tooltip, } from '@posthog/lemon-ui' -import { NotFound } from 'lib/components/NotFound' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Popconfirm, Progress } from 'antd' +import clsx from 'clsx' +import { BindLogic, useActions, useValues } from 'kea' import { Form, Group } from 'kea-forms' +import { router } from 'kea-router' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { EditableField } from 'lib/components/EditableField/EditableField' +import { NotFound } from 'lib/components/NotFound' +import { PageHeader } from 'lib/components/PageHeader' +import { dayjs } from 'lib/dayjs' import { Field } from 'lib/forms/Field' -import { userLogic } from 'scenes/userLogic' -import { ExperimentsPayGate } from './ExperimentsPayGate' +import { IconDelete, IconPlusMini } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonCollapse } from 'lib/lemon-ui/LemonCollapse' -import { EXPERIMENT_INSIGHT_ID } from './constants' +import { Link } from 'lib/lemon-ui/Link' +import { capitalizeFirstLetter, humanFriendlyNumber } from 'lib/utils' +import { useEffect, useState } from 'react' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { Query } from '~/queries/Query/Query' import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' +import { SceneExport } from 'scenes/sceneTypes' import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' -import { ExperimentInsightCreator } from './MetricSelector' -import { More } from 'lib/lemon-ui/LemonButton/More' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { Query } from '~/queries/Query/Query' +import { AvailableFeature, Experiment as ExperimentType, FunnelStep, InsightType } from '~/types' + +import { EXPERIMENT_INSIGHT_ID } from './constants' +import { ExperimentImplementationDetails } from './ExperimentImplementationDetails' +import { experimentLogic, ExperimentLogicProps } from './experimentLogic' +import { ExperimentPreview } from './ExperimentPreview' import { ExperimentResult } from './ExperimentResult' -import clsx from 'clsx' import { getExperimentStatus, getExperimentStatusColor } from './experimentsLogic' +import { ExperimentsPayGate } from './ExperimentsPayGate' +import { ExperimentInsightCreator } from './MetricSelector' +import { SecondaryMetrics } from './SecondaryMetrics' export const scene: SceneExport = { component: Experiment, diff --git a/frontend/src/scenes/experiments/ExperimentImplementationDetails.tsx b/frontend/src/scenes/experiments/ExperimentImplementationDetails.tsx index 65c0651a65159..5dfdd792335a7 100644 --- a/frontend/src/scenes/experiments/ExperimentImplementationDetails.tsx +++ b/frontend/src/scenes/experiments/ExperimentImplementationDetails.tsx @@ -1,6 +1,9 @@ +import { LemonSelect, Link } from '@posthog/lemon-ui' import { IconGolang, IconJavascript, IconNodeJS, IconPHP, IconPython, IconRuby } from 'lib/lemon-ui/icons' import { useState } from 'react' + import { Experiment, MultivariateFlagVariant } from '~/types' + import { GolangSnippet, JSSnippet, @@ -10,7 +13,6 @@ import { RNSnippet, RubySnippet, } from './ExperimentCodeSnippets' -import { LemonSelect, Link } from '@posthog/lemon-ui' interface ExperimentImplementationDetailsProps { experiment: Partial | null diff --git a/frontend/src/scenes/experiments/ExperimentPreview.tsx b/frontend/src/scenes/experiments/ExperimentPreview.tsx index 51de26936b578..9d7585e944e77 100644 --- a/frontend/src/scenes/experiments/ExperimentPreview.tsx +++ b/frontend/src/scenes/experiments/ExperimentPreview.tsx @@ -1,8 +1,16 @@ +import { LemonButton, LemonDivider, LemonModal, Tooltip } from '@posthog/lemon-ui' import { InputNumber, Slider } from 'antd' -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' +import { Field, Form } from 'kea-forms' import { InsightLabel } from 'lib/components/InsightLabel' import { PropertyFilterButton } from 'lib/components/PropertyFilters/components/PropertyFilterButton' +import { TZLabel } from 'lib/components/TZLabel' import { dayjs } from 'lib/dayjs' +import { IconInfo } from 'lib/lemon-ui/icons' +import { humanFriendlyNumber } from 'lib/utils' +import { groupFilters } from 'scenes/feature-flags/FeatureFlags' +import { urls } from 'scenes/urls' + import { ActionFilter as ActionFilterType, AnyPropertyFilter, @@ -10,17 +18,11 @@ import { InsightType, MultivariateFlagVariant, } from '~/types' + +import { EXPERIMENT_EXPOSURE_INSIGHT_ID, EXPERIMENT_INSIGHT_ID } from './constants' import { experimentLogic } from './experimentLogic' import { ExperimentWorkflow } from './ExperimentWorkflow' -import { humanFriendlyNumber } from 'lib/utils' -import { LemonButton, LemonDivider, LemonModal, Tooltip } from '@posthog/lemon-ui' -import { Field, Form } from 'kea-forms' import { MetricSelector } from './MetricSelector' -import { IconInfo } from 'lib/lemon-ui/icons' -import { TZLabel } from 'lib/components/TZLabel' -import { EXPERIMENT_EXPOSURE_INSIGHT_ID, EXPERIMENT_INSIGHT_ID } from './constants' -import { groupFilters } from 'scenes/feature-flags/FeatureFlags' -import { urls } from 'scenes/urls' interface ExperimentPreviewProps { experimentId: number | 'new' diff --git a/frontend/src/scenes/experiments/ExperimentResult.tsx b/frontend/src/scenes/experiments/ExperimentResult.tsx index ccccf7c05a838..1cec85e4b05b6 100644 --- a/frontend/src/scenes/experiments/ExperimentResult.tsx +++ b/frontend/src/scenes/experiments/ExperimentResult.tsx @@ -1,18 +1,21 @@ +import './Experiment.scss' + +import { IconInfo } from '@posthog/icons' +import { Tooltip } from '@posthog/lemon-ui' import { Col, Progress } from 'antd' import { useValues } from 'kea' -import { ChartDisplayType, FilterType, FunnelVizType, InsightShortId, InsightType } from '~/types' -import './Experiment.scss' -import { experimentLogic } from './experimentLogic' -import { FunnelLayout } from 'lib/constants' -import { capitalizeFirstLetter } from 'lib/utils' import { getSeriesColor } from 'lib/colors' import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' -import { NodeKind } from '~/queries/schema' +import { FunnelLayout } from 'lib/constants' +import { capitalizeFirstLetter } from 'lib/utils' + import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' import { Query } from '~/queries/Query/Query' -import { IconInfo } from '@posthog/icons' +import { NodeKind } from '~/queries/schema' +import { ChartDisplayType, FilterType, FunnelVizType, InsightShortId, InsightType } from '~/types' + import { LoadingState } from './Experiment' -import { Tooltip } from '@posthog/lemon-ui' +import { experimentLogic } from './experimentLogic' export function ExperimentResult(): JSX.Element { const { diff --git a/frontend/src/scenes/experiments/ExperimentWorkflow.tsx b/frontend/src/scenes/experiments/ExperimentWorkflow.tsx index 430ab7f0624b9..df0da43e9c100 100644 --- a/frontend/src/scenes/experiments/ExperimentWorkflow.tsx +++ b/frontend/src/scenes/experiments/ExperimentWorkflow.tsx @@ -1,7 +1,8 @@ -import { IconCheckmark, IconRadioButtonUnchecked } from 'lib/lemon-ui/icons' -import { useState } from 'react' import './Experiment.scss' + import clsx from 'clsx' +import { IconCheckmark, IconRadioButtonUnchecked } from 'lib/lemon-ui/icons' +import { useState } from 'react' export function ExperimentWorkflow(): JSX.Element { const [workflowValidateStepCompleted, setWorkflowValidateStepCompleted] = useState(false) diff --git a/frontend/src/scenes/experiments/Experiments.tsx b/frontend/src/scenes/experiments/Experiments.tsx index ee702a4b77451..1a912cb6b240d 100644 --- a/frontend/src/scenes/experiments/Experiments.tsx +++ b/frontend/src/scenes/experiments/Experiments.tsx @@ -1,27 +1,29 @@ -import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' -import { experimentsLogic, getExperimentStatus } from './experimentsLogic' +import { LemonInput, LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' -import { Experiment, ExperimentsTabs, AvailableFeature, ProgressStatus, ProductKey } from '~/types' +import { router } from 'kea-router' +import { ExperimentsHog } from 'lib/components/hedgehogs' +import { PageHeader } from 'lib/components/PageHeader' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' import { normalizeColumnTitle } from 'lib/components/Table/utils' -import { urls } from 'scenes/urls' -import stringWithWBR from 'lib/utils/stringWithWBR' -import { Link } from 'lib/lemon-ui/Link' import { dayjs } from 'lib/dayjs' -import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { userLogic } from 'scenes/userLogic' -import { LemonInput, LemonSelect } from '@posthog/lemon-ui' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { ExperimentsPayGate } from './ExperimentsPayGate' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' -import { router } from 'kea-router' -import { ExperimentsHog } from 'lib/components/hedgehogs' import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Link } from 'lib/lemon-ui/Link' +import stringWithWBR from 'lib/utils/stringWithWBR' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature, Experiment, ExperimentsTabs, ProductKey, ProgressStatus } from '~/types' + import { StatusTag } from './Experiment' +import { experimentsLogic, getExperimentStatus } from './experimentsLogic' +import { ExperimentsPayGate } from './ExperimentsPayGate' export const scene: SceneExport = { component: Experiments, diff --git a/frontend/src/scenes/experiments/ExperimentsPayGate.tsx b/frontend/src/scenes/experiments/ExperimentsPayGate.tsx index 678ad322638ef..0a0ebe684e684 100644 --- a/frontend/src/scenes/experiments/ExperimentsPayGate.tsx +++ b/frontend/src/scenes/experiments/ExperimentsPayGate.tsx @@ -1,4 +1,5 @@ import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' + import { AvailableFeature } from '~/types' export function ExperimentsPayGate(): JSX.Element { diff --git a/frontend/src/scenes/experiments/MetricSelector.tsx b/frontend/src/scenes/experiments/MetricSelector.tsx index 7ac3b5e84d8d9..5b2a09f1e1eec 100644 --- a/frontend/src/scenes/experiments/MetricSelector.tsx +++ b/frontend/src/scenes/experiments/MetricSelector.tsx @@ -1,29 +1,29 @@ -import { BindLogic, useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import './Experiment.scss' +import { IconInfo } from '@posthog/icons' +import { LemonSelect } from '@posthog/lemon-ui' +import { BindLogic, useActions, useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { Attribution } from 'scenes/insights/EditorFilters/AttributionFilter' +import { SamplingFilter } from 'scenes/insights/EditorFilters/SamplingFilter' import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' -import { EditorFilterProps, FilterType, InsightLogicProps, InsightShortId, InsightType } from '~/types' import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' -import { LemonSelect } from '@posthog/lemon-ui' -import { SamplingFilter } from 'scenes/insights/EditorFilters/SamplingFilter' -import { Query } from '~/queries/Query/Query' -import { FunnelsQuery, InsightQueryNode, TrendsQuery } from '~/queries/schema' import { AggregationSelect } from 'scenes/insights/filters/AggregationSelect' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { FunnelConversionWindowFilter } from 'scenes/insights/views/Funnels/FunnelConversionWindowFilter' -import './Experiment.scss' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Attribution } from 'scenes/insights/EditorFilters/AttributionFilter' +import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { TestAccountFilter } from '~/queries/nodes/InsightViz/filters/TestAccountFilter' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Query } from '~/queries/Query/Query' +import { FunnelsQuery, InsightQueryNode, TrendsQuery } from '~/queries/schema' +import { EditorFilterProps, FilterType, InsightLogicProps, InsightShortId, InsightType } from '~/types' + import { DEFAULT_DURATION } from './experimentLogic' -import { IconInfo } from '@posthog/icons' export interface MetricSelectorProps { dashboardItemId: InsightShortId diff --git a/frontend/src/scenes/experiments/SecondaryMetrics.tsx b/frontend/src/scenes/experiments/SecondaryMetrics.tsx index 9c22022c5efa4..b1c710dcb9da1 100644 --- a/frontend/src/scenes/experiments/SecondaryMetrics.tsx +++ b/frontend/src/scenes/experiments/SecondaryMetrics.tsx @@ -1,21 +1,24 @@ -import { useActions, useValues } from 'kea' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { Form } from 'kea-forms' -import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' -import { InsightType } from '~/types' import './Experiment.scss' -import { secondaryMetricsLogic, SecondaryMetricsProps } from './secondaryMetricsLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' -import { IconDelete, IconEdit } from 'lib/lemon-ui/icons' + import { LemonInput, LemonModal, LemonTable } from '@posthog/lemon-ui' -import { Field } from 'lib/forms/Field' -import { MetricSelector } from './MetricSelector' -import { experimentLogic, TabularSecondaryMetricResults } from './experimentLogic' +import { useActions, useValues } from 'kea' +import { Form } from 'kea-forms' import { getSeriesColor } from 'lib/colors' -import { capitalizeFirstLetter, humanFriendlyNumber } from 'lib/utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { Field } from 'lib/forms/Field' +import { IconDelete, IconEdit } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { capitalizeFirstLetter, humanFriendlyNumber } from 'lib/utils' +import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' +import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' + +import { InsightType } from '~/types' + import { SECONDARY_METRIC_INSIGHT_ID } from './constants' +import { experimentLogic, TabularSecondaryMetricResults } from './experimentLogic' +import { MetricSelector } from './MetricSelector' +import { secondaryMetricsLogic, SecondaryMetricsProps } from './secondaryMetricsLogic' export function SecondaryMetrics({ onMetricsChange, diff --git a/frontend/src/scenes/experiments/experimentLogic.test.ts b/frontend/src/scenes/experiments/experimentLogic.test.ts index 0165dec8c4d9b..cf088b3103711 100644 --- a/frontend/src/scenes/experiments/experimentLogic.test.ts +++ b/frontend/src/scenes/experiments/experimentLogic.test.ts @@ -1,10 +1,12 @@ import { expectLogic } from 'kea-test-utils' +import { userLogic } from 'scenes/userLogic' + +import { useAvailableFeatures } from '~/mocks/features' +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' import { AvailableFeature } from '~/types' + import { experimentLogic } from './experimentLogic' -import { useMocks } from '~/mocks/jest' -import { useAvailableFeatures } from '~/mocks/features' -import { userLogic } from 'scenes/userLogic' const RUNNING_EXP_ID = 45 const RUNNING_FUNNEL_EXP_ID = 46 diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx index 7b314e42f979a..af0f013600fbd 100644 --- a/frontend/src/scenes/experiments/experimentLogic.tsx +++ b/frontend/src/scenes/experiments/experimentLogic.tsx @@ -1,47 +1,49 @@ -import { ReactElement } from 'react' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' +import { router, urlToAction } from 'kea-router' import api from 'lib/api' +import { FunnelLayout } from 'lib/constants' import { dayjs } from 'lib/dayjs' +import { IconInfo } from 'lib/lemon-ui/icons' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { ReactElement } from 'react' +import { validateFeatureFlagKey } from 'scenes/feature-flags/featureFlagLogic' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { cleanFilters, getDefaultEvent } from 'scenes/insights/utils/cleanFilters' +import { Scene } from 'scenes/sceneTypes' import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' + +import { groupsModel } from '~/models/groupsModel' +import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { InsightVizNode } from '~/queries/schema' import { + ActionFilter as ActionFilterType, Breadcrumb, + CountPerActorMathType, Experiment, ExperimentResults, FilterType, + FunnelStep, FunnelVizType, InsightType, MultivariateFlagVariant, - TrendResult, - FunnelStep, + PropertyMathType, SecondaryExperimentMetric, SignificanceCode, - CountPerActorMathType, - ActionFilter as ActionFilterType, TrendExperimentVariant, - PropertyMathType, + TrendResult, } from '~/types' + +import { EXPERIMENT_EXPOSURE_INSIGHT_ID, EXPERIMENT_INSIGHT_ID } from './constants' import type { experimentLogicType } from './experimentLogicType' -import { router, urlToAction } from 'kea-router' import { experimentsLogic } from './experimentsLogic' -import { FunnelLayout } from 'lib/constants' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { toParams } from 'lib/utils' -import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { forms } from 'kea-forms' -import { loaders } from 'kea-loaders' -import { IconInfo } from 'lib/lemon-ui/icons' -import { validateFeatureFlagKey } from 'scenes/feature-flags/featureFlagLogic' -import { EXPERIMENT_EXPOSURE_INSIGHT_ID, EXPERIMENT_INSIGHT_ID } from './constants' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { InsightVizNode } from '~/queries/schema' -import { groupsModel } from '~/models/groupsModel' -import { Scene } from 'scenes/sceneTypes' export const DEFAULT_DURATION = 14 // days diff --git a/frontend/src/scenes/experiments/experimentsLogic.ts b/frontend/src/scenes/experiments/experimentsLogic.ts index f76151f5e5b95..b290677205a01 100644 --- a/frontend/src/scenes/experiments/experimentsLogic.ts +++ b/frontend/src/scenes/experiments/experimentsLogic.ts @@ -1,16 +1,18 @@ +import { LemonTagType } from '@posthog/lemon-ui' +import Fuse from 'fuse.js' import { actions, connect, events, kea, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' -import type { experimentsLogicType } from './experimentsLogicType' -import { teamLogic } from 'scenes/teamLogic' -import { AvailableFeature, Experiment, ExperimentsTabs, ProductKey, ProgressStatus } from '~/types' +import { FEATURE_FLAGS } from 'lib/constants' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import Fuse from 'fuse.js' -import { userLogic } from 'scenes/userLogic' -import { subscriptions } from 'kea-subscriptions' -import { loaders } from 'kea-loaders' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { LemonTagType } from '@posthog/lemon-ui' +import { teamLogic } from 'scenes/teamLogic' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature, Experiment, ExperimentsTabs, ProductKey, ProgressStatus } from '~/types' + +import type { experimentsLogicType } from './experimentsLogicType' export function getExperimentStatus(experiment: Experiment): ProgressStatus { if (!experiment.start_date) { diff --git a/frontend/src/scenes/experiments/secondaryMetricsLogic.ts b/frontend/src/scenes/experiments/secondaryMetricsLogic.ts index a746107873357..d3b04d4a29c38 100644 --- a/frontend/src/scenes/experiments/secondaryMetricsLogic.ts +++ b/frontend/src/scenes/experiments/secondaryMetricsLogic.ts @@ -1,20 +1,19 @@ -import { actions, connect, kea, listeners, path, props, key, reducers } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea' import { forms } from 'kea-forms' -import { dayjs } from 'lib/dayjs' - -import { Experiment, FilterType, FunnelVizType, InsightType, SecondaryExperimentMetric } from '~/types' -import { cleanFilters, getDefaultEvent } from 'scenes/insights/utils/cleanFilters' import { FunnelLayout } from 'lib/constants' -import { InsightVizNode } from '~/queries/schema' - -import { SECONDARY_METRIC_INSIGHT_ID } from './constants' -import { insightLogic } from 'scenes/insights/insightLogic' +import { dayjs } from 'lib/dayjs' import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { cleanFilters, getDefaultEvent } from 'scenes/insights/utils/cleanFilters' +import { teamLogic } from 'scenes/teamLogic' + import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { teamLogic } from 'scenes/teamLogic' +import { InsightVizNode } from '~/queries/schema' +import { Experiment, FilterType, FunnelVizType, InsightType, SecondaryExperimentMetric } from '~/types' +import { SECONDARY_METRIC_INSIGHT_ID } from './constants' import type { secondaryMetricsLogicType } from './secondaryMetricsLogicType' const DEFAULT_DURATION = 14 diff --git a/frontend/src/scenes/feature-flags/FeatureFlag.scss b/frontend/src/scenes/feature-flags/FeatureFlag.scss index 0b72506c172cb..319512c5f7670 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlag.scss +++ b/frontend/src/scenes/feature-flags/FeatureFlag.scss @@ -64,6 +64,10 @@ } .FeatureConditionCard { + .posthog-3000 & { + background: var(--bg-light); + } + .FeatureConditionCard--border--highlight { border-color: var(--primary-3000); } diff --git a/frontend/src/scenes/feature-flags/FeatureFlag.tsx b/frontend/src/scenes/feature-flags/FeatureFlag.tsx index 9944499e472fa..8c2ef3d75606e 100644 --- a/frontend/src/scenes/feature-flags/FeatureFlag.tsx +++ b/frontend/src/scenes/feature-flags/FeatureFlag.tsx @@ -1,72 +1,75 @@ -import { useEffect, useState } from 'react' -import { Form, Group } from 'kea-forms' -import { Radio, Popconfirm, Skeleton, Card } from 'antd' +import './FeatureFlag.scss' + +import { Card, Popconfirm, Radio, Skeleton } from 'antd' import { useActions, useValues } from 'kea' -import { alphabet, capitalizeFirstLetter } from 'lib/utils' -import { featureFlagLogic } from './featureFlagLogic' -import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic' +import { Form, Group } from 'kea-forms' +import { router } from 'kea-router' +import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { NotFound } from 'lib/components/NotFound' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' import { PageHeader } from 'lib/components/PageHeader' -import './FeatureFlag.scss' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { FEATURE_FLAGS } from 'lib/constants' +import { Field } from 'lib/forms/Field' import { IconDelete, IconLock, IconPlus, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' +import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' +import { Link } from 'lib/lemon-ui/Link' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { SceneExport } from 'scenes/sceneTypes' +import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic' +import { alphabet, capitalizeFirstLetter } from 'lib/utils' +import { PostHogFeature } from 'posthog-js/react' +import { useEffect, useState } from 'react' +import { billingLogic } from 'scenes/billing/billingLogic' +import { Dashboard } from 'scenes/dashboard/Dashboard' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { EmptyDashboardComponent } from 'scenes/dashboard/EmptyDashboardComponent' import { UTM_TAGS } from 'scenes/feature-flags/FeatureFlagSnippets' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { JSONEditorInput } from 'scenes/feature-flags/JSONEditorInput' +import { concatWithPunctuation } from 'scenes/insights/utils' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' +import { ResourcePermission } from 'scenes/ResourcePermissionModal' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + +import { tagsModel } from '~/models/tagsModel' +import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' +import { Query } from '~/queries/Query/Query' +import { NodeKind } from '~/queries/schema' import { AnyPropertyFilter, AvailableFeature, DashboardPlacement, + FeatureFlagGroupType, + FeatureFlagType, + NotebookNodeType, PropertyFilterType, PropertyOperator, - Resource, - FeatureFlagType, ReplayTabs, - FeatureFlagGroupType, - NotebookNodeType, + Resource, } from '~/types' -import { Link } from 'lib/lemon-ui/Link' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { Field } from 'lib/forms/Field' -import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { urls } from 'scenes/urls' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { router } from 'kea-router' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' -import { FEATURE_FLAGS } from 'lib/constants' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { FeatureFlagsTab, featureFlagsLogic } from './featureFlagsLogic' -import { RecentFeatureFlagInsights } from './RecentFeatureFlagInsightsCard' -import { NotFound } from 'lib/components/NotFound' + +import { AnalysisTab } from './FeatureFlagAnalysisTab' import { FeatureFlagAutoRollback } from './FeatureFlagAutoRollout' -import { featureFlagPermissionsLogic } from './featureFlagPermissionsLogic' -import { ResourcePermission } from 'scenes/ResourcePermissionModal' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' -import { JSONEditorInput } from 'scenes/feature-flags/JSONEditorInput' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { tagsModel } from '~/models/tagsModel' -import { Dashboard } from 'scenes/dashboard/Dashboard' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' -import { EmptyDashboardComponent } from 'scenes/dashboard/EmptyDashboardComponent' import { FeatureFlagCodeExample } from './FeatureFlagCodeExample' -import { billingLogic } from 'scenes/billing/billingLogic' -import { organizationLogic } from '../organizationLogic' -import { AnalysisTab } from './FeatureFlagAnalysisTab' -import { NodeKind } from '~/queries/schema' -import { Query } from '~/queries/Query/Query' -import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' -import { PostHogFeature } from 'posthog-js/react' -import { concatWithPunctuation } from 'scenes/insights/utils' -import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { FeatureFlagReleaseConditions } from './FeatureFlagReleaseConditions' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' +import { featureFlagLogic } from './featureFlagLogic' +import { featureFlagPermissionsLogic } from './featureFlagPermissionsLogic' import FeatureFlagProjects from './FeatureFlagProjects' +import { FeatureFlagReleaseConditions } from './FeatureFlagReleaseConditions' +import { featureFlagsLogic, FeatureFlagsTab } from './featureFlagsLogic' +import { RecentFeatureFlagInsights } from './RecentFeatureFlagInsightsCard' export const scene: SceneExport = { component: FeatureFlag, @@ -84,10 +87,17 @@ function focusVariantKeyField(index: number): void { } export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { - const { props, featureFlag, featureFlagLoading, featureFlagMissing, isEditingFlag, recordingFilterForFlag } = - useValues(featureFlagLogic) + const { + props, + featureFlag, + featureFlagLoading, + featureFlagMissing, + isEditingFlag, + recordingFilterForFlag, + newCohortLoading, + } = useValues(featureFlagLogic) const { featureFlags } = useValues(enabledFeaturesLogic) - const { deleteFeatureFlag, editFeatureFlag, loadFeatureFlag, triggerFeatureFlagUpdate } = + const { deleteFeatureFlag, editFeatureFlag, loadFeatureFlag, triggerFeatureFlagUpdate, createStaticCohort } = useActions(featureFlagLogic) const { addableRoles, unfilteredAddableRolesLoading, rolesToAdd, derivedRoles } = useValues( @@ -97,8 +107,6 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { featureFlagPermissionsLogic({ flagId: featureFlag.id }) ) - const { currentOrganization } = useValues(organizationLogic) - const { tags } = useValues(tagsModel) const { hasAvailableFeature } = useValues(userLogic) @@ -155,8 +163,7 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { }) } - const hasMultipleProjects = (currentOrganization?.teams?.length ?? 0) > 1 - if (featureFlags[FEATURE_FLAGS.MULTI_PROJECT_FEATURE_FLAGS] && hasMultipleProjects) { + if (featureFlags[FEATURE_FLAGS.MULTI_PROJECT_FEATURE_FLAGS]) { tabs.push({ label: 'Projects', key: FeatureFlagsTab.PROJECTS, @@ -527,6 +534,58 @@ export function FeatureFlag({ id }: { id?: string } = {}): JSX.Element { buttons={ <>

    + + + View Recordings + + {featureFlags[ + FEATURE_FLAGS.FEATURE_FLAG_COHORT_CREATION + ] && ( + { + createStaticCohort() + }} + fullWidth + > + Create Cohort + + )} + + { + deleteFeatureFlag(featureFlag) + }} + disabledReason={ + featureFlagLoading + ? 'Loading...' + : !featureFlag.can_edit + ? "You have only 'View' access for this feature flag. To make changes, please contact the flag's creator." + : (featureFlag.features?.length || 0) > 0 + ? 'This feature flag is in use with an early access feature. Delete the early access feature to delete this flag' + : (featureFlag.experiment_set?.length || 0) > 0 + ? 'This feature flag is linked to an experiment. Delete the experiment to delete this flag' + : null + } + > + Delete feature flag + + + } + /> + - - View Recordings - - - { - deleteFeatureFlag(featureFlag) - }} - disabledReason={ - featureFlagLoading - ? 'Loading...' - : !featureFlag.can_edit - ? "You have only 'View' access for this feature flag. To make changes, please contact the flag's creator." - : (featureFlag.features?.length || 0) > 0 - ? 'This feature flag is in use with an early access feature. Delete the early access feature to delete this flag' - : (featureFlag.experiment_set?.length || 0) > 0 - ? 'This feature flag is linked to an experiment. Delete the experiment to delete this flag' - : null - } - > - Delete feature flag - => { const { currentTeamId } = useValues(teamLogic) @@ -77,71 +80,106 @@ const getColumns = (): LemonTableColumns => { ] } -export default function FeatureFlagProjects(): JSX.Element { +function InfoBanner(): JSX.Element { + const { currentOrganization } = useValues(organizationLogic) + const { featureFlag } = useValues(featureFlagLogic) + const hasMultipleProjects = (currentOrganization?.teams?.length ?? 0) > 1 + + const isMember = + !currentOrganization?.membership_level || + currentOrganization.membership_level < OrganizationMembershipLevel.Admin + + let text + + if (isMember && !hasMultipleProjects) { + text = `You currently have access to only one project. If your organization manages multiple projects and you wish to copy this feature flag across them, request project access from your administrator.` + } else if (!hasMultipleProjects) { + text = `This feature enables the copying of a feature flag across different projects. Once additional projects are added within your organization, you'll be able to replicate this flag to them.` + } else if (!featureFlag.can_edit) { + text = `You don't have the necessary permissions to copy this flag to another project. Contact your administrator to request editing rights.` + } else { + return <> + } + + return ( + + {text} + + ) +} + +function FeatureFlagCopySection(): JSX.Element { const { featureFlag, copyDestinationProject, projectsWithCurrentFlag, featureFlagCopyLoading } = useValues(featureFlagLogic) - const { setCopyDestinationProject, loadProjectsWithCurrentFlag, copyFlag } = useActions(featureFlagLogic) + const { setCopyDestinationProject, copyFlag } = useActions(featureFlagLogic) const { currentOrganization } = useValues(organizationLogic) const { currentTeam } = useValues(teamLogic) + const hasMultipleProjects = (currentOrganization?.teams?.length ?? 0) > 1 + + return hasMultipleProjects && featureFlag.can_edit ? ( + <> +

    Feature flag copy

    +
    Copy your flag and its configuration to another project.
    +
    +
    +
    Key
    +
    + {featureFlag.key} +
    +
    +
    +
    + +
    +
    +
    Destination project
    + setCopyDestinationProject(id)} + options={ + currentOrganization?.teams + ?.map((team) => ({ value: team.id, label: team.name })) + .filter((option) => option.value !== currentTeam?.id) || [] + } + className="min-w-40" + /> +
    +
    +
    + } + onClick={() => copyFlag()} + className="w-28 max-w-28" + > + {projectsWithCurrentFlag.find((p) => Number(p.team_id) === copyDestinationProject) + ? 'Update' + : 'Copy'} + +
    +
    + + ) : ( + <> + ) +} + +export default function FeatureFlagProjects(): JSX.Element { + const { projectsWithCurrentFlag } = useValues(featureFlagLogic) + const { loadProjectsWithCurrentFlag } = useActions(featureFlagLogic) + useEffect(() => { loadProjectsWithCurrentFlag() }, []) return (
    - {featureFlag.can_edit ? ( - <> -

    Feature flag copy

    -
    Copy your flag and its configuration to another project.
    -
    -
    -
    Key
    -
    - {featureFlag.key} -
    -
    -
    -
    - -
    -
    -
    Destination project
    - setCopyDestinationProject(id)} - options={ - currentOrganization?.teams - ?.map((team) => ({ value: team.id, label: team.name })) - .filter((option) => option.value !== currentTeam?.id) || [] - } - className="min-w-40" - /> -
    -
    -
    - } - onClick={() => copyFlag()} - className="w-28 max-w-28" - > - {projectsWithCurrentFlag.find((p) => Number(p.team_id) === copyDestinationProject) - ? 'Update' - : 'Copy'} - -
    -
    - - ) : ( - - You currently cannot copy this flag to another project. Contact your administrator to request - editing rights. - - )} + +
    void diff --git a/frontend/src/scenes/feature-flags/RecentFeatureFlagInsightsCard.tsx b/frontend/src/scenes/feature-flags/RecentFeatureFlagInsightsCard.tsx index 5fdf19a9a4cb6..225a69bdbf761 100644 --- a/frontend/src/scenes/feature-flags/RecentFeatureFlagInsightsCard.tsx +++ b/frontend/src/scenes/feature-flags/RecentFeatureFlagInsightsCard.tsx @@ -2,7 +2,9 @@ import { useValues } from 'kea' import { CompactList } from 'lib/components/CompactList/CompactList' import { InsightRow } from 'scenes/project-homepage/RecentInsights' import { urls } from 'scenes/urls' + import { InsightModel } from '~/types' + import { featureFlagLogic } from './featureFlagLogic' export function RecentFeatureFlagInsights(): JSX.Element { diff --git a/frontend/src/scenes/feature-flags/activityDescriptions.tsx b/frontend/src/scenes/feature-flags/activityDescriptions.tsx index e7f90ab9c628c..d2b3b754aaf50 100644 --- a/frontend/src/scenes/feature-flags/activityDescriptions.tsx +++ b/frontend/src/scenes/feature-flags/activityDescriptions.tsx @@ -6,13 +6,14 @@ import { detectBoolean, HumanizedChange, } from 'lib/components/ActivityLog/humanizeActivity' +import { SentenceList } from 'lib/components/ActivityLog/SentenceList' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { PropertyFilterButton } from 'lib/components/PropertyFilters/components/PropertyFilterButton' import { Link } from 'lib/lemon-ui/Link' +import { pluralize } from 'lib/utils' import { urls } from 'scenes/urls' + import { AnyPropertyFilter, FeatureFlagFilters, FeatureFlagGroupType, FeatureFlagType } from '~/types' -import { pluralize } from 'lib/utils' -import { SentenceList } from 'lib/components/ActivityLog/SentenceList' -import { PropertyFilterButton } from 'lib/components/PropertyFilters/components/PropertyFilterButton' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' const nameOrLinkToFlag = (id: string | undefined, name: string | null | undefined): string | JSX.Element => { // detail.name diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.test.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.test.ts index db6df537e3eac..e4d21cb4b7c64 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.test.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.test.ts @@ -1,8 +1,9 @@ -import { initKeaTests } from '~/test/init' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' import { expectLogic } from 'kea-test-utils' -import { useMocks } from '~/mocks/jest' import api from 'lib/api' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' import { FeatureFlagGroupType, FeatureFlagType, diff --git a/frontend/src/scenes/feature-flags/featureFlagLogic.ts b/frontend/src/scenes/feature-flags/featureFlagLogic.ts index 2c34da9925380..742eff1b44cb8 100644 --- a/frontend/src/scenes/feature-flags/featureFlagLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagLogic.ts @@ -1,9 +1,35 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import type { featureFlagLogicType } from './featureFlagLogicType' +import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' +import { router, urlToAction } from 'kea-router' +import api from 'lib/api' +import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { dayjs } from 'lib/dayjs' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { sum, toParams } from 'lib/utils' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic' +import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' +import { NEW_EARLY_ACCESS_FEATURE } from 'scenes/early-access-features/earlyAccessFeatureLogic' +import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' +import { filterTrendsClientSideParams } from 'scenes/insights/sharedUtils' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { Scene } from 'scenes/sceneTypes' +import { NEW_SURVEY, NewSurvey } from 'scenes/surveys/constants' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + +import { groupsModel } from '~/models/groupsModel' import { AnyPropertyFilter, AvailableFeature, Breadcrumb, + CohortType, + DashboardBasicType, + EarlyAccessFeatureType, + FeatureFlagGroupType, FeatureFlagRollbackConditions, FeatureFlagType, FilterType, @@ -11,43 +37,20 @@ import { InsightType, MultivariateFlagOptions, MultivariateFlagVariant, + NewEarlyAccessFeatureType, + OrganizationFeatureFlag, PropertyFilterType, PropertyOperator, RolloutConditionType, - FeatureFlagGroupType, - UserBlastRadiusType, - DashboardBasicType, - NewEarlyAccessFeatureType, - EarlyAccessFeatureType, Survey, SurveyQuestionType, - OrganizationFeatureFlag, + UserBlastRadiusType, } from '~/types' -import api from 'lib/api' -import { router, urlToAction } from 'kea-router' -import { sum, toParams } from 'lib/utils' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { urls } from 'scenes/urls' + +import { organizationLogic } from '../organizationLogic' import { teamLogic } from '../teamLogic' -import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' -import { groupsModel } from '~/models/groupsModel' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { loaders } from 'kea-loaders' -import { forms } from 'kea-forms' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' -import { dayjs } from 'lib/dayjs' -import { filterTrendsClientSideParams } from 'scenes/insights/sharedUtils' +import type { featureFlagLogicType } from './featureFlagLogicType' import { featureFlagPermissionsLogic } from './featureFlagPermissionsLogic' -import { userLogic } from 'scenes/userLogic' -import { newDashboardLogic } from 'scenes/dashboard/newDashboardLogic' -import { dashboardsLogic } from 'scenes/dashboard/dashboards/dashboardsLogic' -import { organizationLogic } from '../organizationLogic' -import { NEW_EARLY_ACCESS_FEATURE } from 'scenes/early-access-features/earlyAccessFeatureLogic' -import { NEW_SURVEY, NewSurvey } from 'scenes/surveys/constants' -import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' -import { Scene } from 'scenes/sceneTypes' const getDefaultRollbackCondition = (): FeatureFlagRollbackConditions => ({ operator: 'gt', @@ -580,6 +583,17 @@ export const featureFlagLogic = kea([ }, }, ], + newCohort: [ + null as CohortType | null, + { + createStaticCohort: async () => { + if (props.id && props.id !== 'new' && props.id !== 'link') { + return (await api.featureFlags.createStaticCohort(props.id)).cohort + } + return null + }, + }, + ], projectsWithCurrentFlag: { __default: [] as OrganizationFeatureFlag[], loadProjectsWithCurrentFlag: async () => { @@ -790,6 +804,16 @@ export const featureFlagLogic = kea([ actions.loadProjectsWithCurrentFlag() actions.setCopyDestinationProject(null) }, + createStaticCohortSuccess: ({ newCohort }) => { + if (newCohort) { + lemonToast.success('Static cohort created successfully', { + button: { + label: 'View cohort', + action: () => router.actions.push(urls.cohort(newCohort.id)), + }, + }) + } + }, })), selectors({ sentryErrorCount: [(s) => [s.sentryStats], (stats) => stats.total_count], diff --git a/frontend/src/scenes/feature-flags/featureFlagPermissionsLogic.tsx b/frontend/src/scenes/feature-flags/featureFlagPermissionsLogic.tsx index 43c0733e877e7..34f78193ae1f8 100644 --- a/frontend/src/scenes/feature-flags/featureFlagPermissionsLogic.tsx +++ b/frontend/src/scenes/feature-flags/featureFlagPermissionsLogic.tsx @@ -3,6 +3,7 @@ import { loaders } from 'kea-loaders' import api from 'lib/api' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { rolesLogic } from 'scenes/settings/organization/Permissions/Roles/rolesLogic' + import { AccessLevel, FeatureFlagAssociatedRoleType, Resource, RoleType } from '~/types' import type { featureFlagPermissionsLogicType } from './featureFlagPermissionsLogicType' diff --git a/frontend/src/scenes/feature-flags/featureFlagsLogic.test.ts b/frontend/src/scenes/feature-flags/featureFlagsLogic.test.ts index d1b64c65710d4..ce39c7481bc12 100644 --- a/frontend/src/scenes/feature-flags/featureFlagsLogic.test.ts +++ b/frontend/src/scenes/feature-flags/featureFlagsLogic.test.ts @@ -1,9 +1,10 @@ -import { initKeaTests } from '~/test/init' -import { featureFlagsLogic, FeatureFlagsTab } from 'scenes/feature-flags/featureFlagsLogic' -import { expectLogic } from 'kea-test-utils' import { router } from 'kea-router' +import { expectLogic } from 'kea-test-utils' +import { featureFlagsLogic, FeatureFlagsTab } from 'scenes/feature-flags/featureFlagsLogic' import { urls } from 'scenes/urls' +import { initKeaTests } from '~/test/init' + describe('the feature flags logic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/feature-flags/featureFlagsLogic.ts b/frontend/src/scenes/feature-flags/featureFlagsLogic.ts index da81fca11890e..e196a65ac98b5 100644 --- a/frontend/src/scenes/feature-flags/featureFlagsLogic.ts +++ b/frontend/src/scenes/feature-flags/featureFlagsLogic.ts @@ -1,14 +1,16 @@ +import Fuse from 'fuse.js' +import { actions, connect, events, kea, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, path, connect, actions, reducers, selectors, listeners, events } from 'kea' +import { actionToUrl, router, urlToAction } from 'kea-router' import api from 'lib/api' -import Fuse from 'fuse.js' -import type { featureFlagsLogicType } from './featureFlagsLogicType' -import { Breadcrumb, FeatureFlagType } from '~/types' -import { teamLogic } from '../teamLogic' -import { urls } from 'scenes/urls' -import { router, actionToUrl, urlToAction } from 'kea-router' import { LemonSelectOption } from 'lib/lemon-ui/LemonSelect' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb, FeatureFlagType } from '~/types' + +import { teamLogic } from '../teamLogic' +import type { featureFlagsLogicType } from './featureFlagsLogicType' export enum FeatureFlagsTab { OVERVIEW = 'overview', diff --git a/frontend/src/scenes/feedback/Feedback.stories.tsx b/frontend/src/scenes/feedback/Feedback.stories.tsx index 1fb673c9d23e0..d767eb8ba26aa 100644 --- a/frontend/src/scenes/feedback/Feedback.stories.tsx +++ b/frontend/src/scenes/feedback/Feedback.stories.tsx @@ -3,18 +3,20 @@ import { router } from 'kea-router' import { useEffect } from 'react' import { App } from 'scenes/App' import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' + import { feedbackLogic } from './feedbackLogic' import { inAppFeedbackLogic } from './inAppFeedbackLogic' import { userInterviewSchedulerLogic } from './userInterviewSchedulerLogic' const meta: Meta = { title: 'Scenes-App/Feedback', + tags: ['test-skip'], // FIXME: Use mockdate in this story parameters: { layout: 'fullscreen', testOptions: { excludeNavigationFromSnapshot: true, - skip: true, // FIXME: Use mockdate in this story }, viewMode: 'story', // Might need to add a mockdate here, however when I do it breaks the page diff --git a/frontend/src/scenes/feedback/Feedback.tsx b/frontend/src/scenes/feedback/Feedback.tsx index e697b5003f08d..38da1029a2457 100644 --- a/frontend/src/scenes/feedback/Feedback.tsx +++ b/frontend/src/scenes/feedback/Feedback.tsx @@ -1,11 +1,12 @@ import { LemonTag } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' import { SceneExport } from 'scenes/sceneTypes' + import { feedbackLogic } from './feedbackLogic' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' import { InAppFeedback, InAppFeedbackHeaderButtons } from './InAppFeedback' import { UserInterviewScheduler, UserInterviewSchedulerHeaderButtons } from './UserInterviewScheduler' -import { useActions, useValues } from 'kea' export const Feedback = (): JSX.Element => { const { activeTab } = useValues(feedbackLogic) diff --git a/frontend/src/scenes/feedback/InAppFeedback.tsx b/frontend/src/scenes/feedback/InAppFeedback.tsx index bdb1347715ecd..15d52352d6a9a 100644 --- a/frontend/src/scenes/feedback/InAppFeedback.tsx +++ b/frontend/src/scenes/feedback/InAppFeedback.tsx @@ -1,11 +1,13 @@ -import { LemonButton, LemonCollapse, LemonDivider, LemonModal, Link } from '@posthog/lemon-ui' import { urls } from '@posthog/apps-common' +import { LemonButton, LemonCollapse, LemonDivider, LemonModal, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { inAppFeedbackLogic } from './inAppFeedbackLogic' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { IconHelpOutline } from 'lib/lemon-ui/icons' + import { Query } from '~/queries/Query/Query' +import { inAppFeedbackLogic } from './inAppFeedbackLogic' + const OPT_IN_SNIPPET = `posthog.init('YOUR_PROJECT_API_KEY', { api_host: 'YOUR API HOST', opt_in_site_apps: true // <--- Add this line diff --git a/frontend/src/scenes/feedback/UserInterviewScheduler.tsx b/frontend/src/scenes/feedback/UserInterviewScheduler.tsx index 6b9105f4cae09..a1539d13fa273 100644 --- a/frontend/src/scenes/feedback/UserInterviewScheduler.tsx +++ b/frontend/src/scenes/feedback/UserInterviewScheduler.tsx @@ -1,16 +1,15 @@ -import { LemonButton, LemonCollapse, LemonInput, LemonModal, LemonTextArea, Link } from '@posthog/lemon-ui' +import './UserInterviewScheduler.scss' import { urls } from '@posthog/apps-common' +import { LemonButton, LemonCollapse, LemonInput, LemonModal, LemonTextArea, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { Form } from 'kea-forms' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' - +import { Field } from 'lib/forms/Field' import { IconHelpOutline } from 'lib/lemon-ui/icons' -import { FLAG_PREFIX, userInterviewSchedulerLogic } from './userInterviewSchedulerLogic' import { OverViewTab } from 'scenes/feature-flags/FeatureFlags' -import { Form } from 'kea-forms' -import './UserInterviewScheduler.scss' -import { Field } from 'lib/forms/Field' +import { FLAG_PREFIX, userInterviewSchedulerLogic } from './userInterviewSchedulerLogic' const OPT_IN_SNIPPET = `posthog.init('YOUR_PROJECT_API_KEY', { api_host: 'YOUR API HOST', diff --git a/frontend/src/scenes/feedback/inAppFeedbackLogic.ts b/frontend/src/scenes/feedback/inAppFeedbackLogic.ts index d6183c4b98d27..646947084ab2b 100644 --- a/frontend/src/scenes/feedback/inAppFeedbackLogic.ts +++ b/frontend/src/scenes/feedback/inAppFeedbackLogic.ts @@ -1,11 +1,12 @@ -import { EventsQuery, InsightVizNode } from '../../queries/schema' import { actions, afterMount, kea, path, reducers } from 'kea' +import { loaders } from 'kea-loaders' +import api from 'lib/api' + import { DataTableNode, Node, NodeKind, QuerySchema, TrendsQuery } from '~/queries/schema' +import { EventType } from '~/types' +import { EventsQuery, InsightVizNode } from '../../queries/schema' import type { inAppFeedbackLogicType } from './inAppFeedbackLogicType' -import api from 'lib/api' -import { loaders } from 'kea-loaders' -import { EventType } from '~/types' const EVENT_NAME = 'Feedback Sent' const FEEDBACK_PROPERTY = '$feedback' diff --git a/frontend/src/scenes/feedback/userInterviewSchedulerLogic.ts b/frontend/src/scenes/feedback/userInterviewSchedulerLogic.ts index 8273634fc716c..a9413936c8828 100644 --- a/frontend/src/scenes/feedback/userInterviewSchedulerLogic.ts +++ b/frontend/src/scenes/feedback/userInterviewSchedulerLogic.ts @@ -1,8 +1,10 @@ -import { isURL } from 'lib/utils' import { actions, kea, path, reducers, selectors } from 'kea' import { forms } from 'kea-forms' +import { isURL } from 'lib/utils' import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' + import { FeatureFlagGroupType, FeatureFlagType, PropertyFilterType, PropertyOperator } from '~/types' + import type { userInterviewSchedulerLogicType } from './userInterviewSchedulerLogicType' export const FLAG_PREFIX = 'interview-' diff --git a/frontend/src/scenes/funnels/Funnel.tsx b/frontend/src/scenes/funnels/Funnel.tsx index 86668abfcfabf..167841f982f08 100644 --- a/frontend/src/scenes/funnels/Funnel.tsx +++ b/frontend/src/scenes/funnels/Funnel.tsx @@ -1,15 +1,16 @@ import './Funnel.scss' -import { useValues } from 'kea' +import { useValues } from 'kea' +import { FunnelLayout } from 'lib/constants' +import { FunnelLineGraph } from 'scenes/funnels/FunnelLineGraph' import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelDataLogic } from './funnelDataLogic' import { ChartParams, FunnelVizType } from '~/types' -import { FunnelLayout } from 'lib/constants' -import { FunnelHistogram } from './FunnelHistogram' -import { FunnelLineGraph } from 'scenes/funnels/FunnelLineGraph' + import { FunnelBarChart } from './FunnelBarChart/FunnelBarChart' import { FunnelBarGraph } from './FunnelBarGraph/FunnelBarGraph' +import { funnelDataLogic } from './funnelDataLogic' +import { FunnelHistogram } from './FunnelHistogram' export function Funnel(props: ChartParams): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/funnels/FunnelBarChart/FunnelBarChart.tsx b/frontend/src/scenes/funnels/FunnelBarChart/FunnelBarChart.tsx index a33c1b5cea4e7..ac2dbc2342804 100644 --- a/frontend/src/scenes/funnels/FunnelBarChart/FunnelBarChart.tsx +++ b/frontend/src/scenes/funnels/FunnelBarChart/FunnelBarChart.tsx @@ -1,17 +1,20 @@ -import { useValues } from 'kea' -import { useMemo } from 'react' import './FunnelBarChart.scss' -import { ChartParams } from '~/types' + import clsx from 'clsx' -import { useScrollable } from 'lib/hooks/useScrollable' +import { useValues } from 'kea' import { useResizeObserver } from 'lib/hooks/useResizeObserver' -import { useFunnelTooltip } from '../useFunnelTooltip' -import { StepLegend } from './StepLegend' -import { StepBars } from './StepBars' -import { StepBarLabels } from './StepBarLabels' +import { useScrollable } from 'lib/hooks/useScrollable' +import { useMemo } from 'react' import { insightLogic } from 'scenes/insights/insightLogic' + +import { ChartParams } from '~/types' + import { funnelDataLogic } from '../funnelDataLogic' import { funnelPersonsModalLogic } from '../funnelPersonsModalLogic' +import { useFunnelTooltip } from '../useFunnelTooltip' +import { StepBarLabels } from './StepBarLabels' +import { StepBars } from './StepBars' +import { StepLegend } from './StepLegend' interface FunnelBarChartCSSProperties extends React.CSSProperties { '--bar-width': string diff --git a/frontend/src/scenes/funnels/FunnelBarChart/StepBar.tsx b/frontend/src/scenes/funnels/FunnelBarChart/StepBar.tsx index 8531f3c8ab1c3..8c4f301a87c9f 100644 --- a/frontend/src/scenes/funnels/FunnelBarChart/StepBar.tsx +++ b/frontend/src/scenes/funnels/FunnelBarChart/StepBar.tsx @@ -1,13 +1,15 @@ +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { useRef } from 'react' -import { FunnelStepWithConversionMetrics } from '~/types' -import { percentage } from 'lib/utils' import { getSeriesColor } from 'lib/colors' -import clsx from 'clsx' -import { funnelTooltipLogic } from '../funnelTooltipLogic' +import { percentage } from 'lib/utils' +import { useRef } from 'react' import { insightLogic } from 'scenes/insights/insightLogic' + +import { FunnelStepWithConversionMetrics } from '~/types' + import { funnelDataLogic } from '../funnelDataLogic' import { funnelPersonsModalLogic } from '../funnelPersonsModalLogic' +import { funnelTooltipLogic } from '../funnelTooltipLogic' export interface StepBarProps { step: FunnelStepWithConversionMetrics diff --git a/frontend/src/scenes/funnels/FunnelBarChart/StepBars.tsx b/frontend/src/scenes/funnels/FunnelBarChart/StepBars.tsx index 82c47ef9c66bc..b0f3d522093da 100644 --- a/frontend/src/scenes/funnels/FunnelBarChart/StepBars.tsx +++ b/frontend/src/scenes/funnels/FunnelBarChart/StepBars.tsx @@ -1,5 +1,6 @@ import clsx from 'clsx' -import { StepBarProps, StepBar } from './StepBar' + +import { StepBar, StepBarProps } from './StepBar' export function StepBars({ step, stepIndex, showPersonsModal }: Omit): JSX.Element { return ( diff --git a/frontend/src/scenes/funnels/FunnelBarChart/StepLegend.tsx b/frontend/src/scenes/funnels/FunnelBarChart/StepLegend.tsx index 08bed0d6fec05..820ec035a8d3b 100644 --- a/frontend/src/scenes/funnels/FunnelBarChart/StepLegend.tsx +++ b/frontend/src/scenes/funnels/FunnelBarChart/StepLegend.tsx @@ -1,18 +1,20 @@ import { useActions, useValues } from 'kea' -import { AvailableFeature, ChartParams, FunnelStepWithConversionMetrics } from '~/types' -import { LemonRow } from 'lib/lemon-ui/LemonRow' -import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' -import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' import { IconSchedule, IconTrendingFlat, IconTrendingFlatDown } from 'lib/lemon-ui/icons' +import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { capitalizeFirstLetter, humanFriendlyDuration, percentage, pluralize } from 'lib/utils' -import { ValueInspectorButton } from '../ValueInspectorButton' -import { FunnelStepMore } from '../FunnelStepMore' -import { userLogic } from 'scenes/userLogic' -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { insightLogic } from 'scenes/insights/insightLogic' +import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature, ChartParams, FunnelStepWithConversionMetrics } from '~/types' + import { funnelPersonsModalLogic } from '../funnelPersonsModalLogic' +import { FunnelStepMore } from '../FunnelStepMore' +import { ValueInspectorButton } from '../ValueInspectorButton' type StepLegendProps = { step: FunnelStepWithConversionMetrics diff --git a/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx b/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx index 9fd86c95b9dd6..5b77c77556392 100644 --- a/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx +++ b/frontend/src/scenes/funnels/FunnelBarGraph/Bar.tsx @@ -1,11 +1,13 @@ -import { useEffect, useRef, useState } from 'react' +import { LemonDropdown } from '@posthog/lemon-ui' +import { getSeriesColor } from 'lib/colors' import { capitalizeFirstLetter, percentage } from 'lib/utils' +import { useEffect, useRef, useState } from 'react' import { LEGACY_InsightTooltip } from 'scenes/insights/InsightTooltip/LEGACY_InsightTooltip' -import { getSeriesPositionName } from '../funnelUtils' -import { getSeriesColor } from 'lib/colors' + import { Noun } from '~/models/groupsModel' + +import { getSeriesPositionName } from '../funnelUtils' import { MetricRow } from './MetricRow' -import { LemonDropdown } from '@posthog/lemon-ui' interface BarProps { percentage: number diff --git a/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx b/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx index 4ba26d99cac76..10b44978f1cf8 100644 --- a/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx +++ b/frontend/src/scenes/funnels/FunnelBarGraph/FunnelBarGraph.tsx @@ -1,22 +1,25 @@ -import clsx from 'clsx' -import { humanFriendlyDuration, percentage, pluralize } from 'lib/utils' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { SeriesGlyph } from 'lib/components/SeriesGlyph' -import { IconTrendingFlatDown, IconInfinity, IconTrendingFlat } from 'lib/lemon-ui/icons' import './FunnelBarGraph.scss' + +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { getBreakdownMaxIndex, getReferenceStep } from '../funnelUtils' -import { ChartParams, FunnelStepReference, StepOrderValue } from '~/types' import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' -import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { SeriesGlyph } from 'lib/components/SeriesGlyph' import { useResizeObserver } from 'lib/hooks/useResizeObserver' -import { FunnelStepMore } from '../FunnelStepMore' -import { ValueInspectorButton } from '../ValueInspectorButton' -import { DuplicateStepIndicator } from './DuplicateStepIndicator' -import { Bar } from './Bar' +import { IconInfinity, IconTrendingFlat, IconTrendingFlatDown } from 'lib/lemon-ui/icons' +import { humanFriendlyDuration, percentage, pluralize } from 'lib/utils' import { insightLogic } from 'scenes/insights/insightLogic' +import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' + +import { ChartParams, FunnelStepReference, StepOrderValue } from '~/types' + import { funnelDataLogic } from '../funnelDataLogic' import { funnelPersonsModalLogic } from '../funnelPersonsModalLogic' +import { FunnelStepMore } from '../FunnelStepMore' +import { getBreakdownMaxIndex, getReferenceStep } from '../funnelUtils' +import { ValueInspectorButton } from '../ValueInspectorButton' +import { Bar } from './Bar' +import { DuplicateStepIndicator } from './DuplicateStepIndicator' export function FunnelBarGraph({ inCardView, diff --git a/frontend/src/scenes/funnels/FunnelCanvasLabel.tsx b/frontend/src/scenes/funnels/FunnelCanvasLabel.tsx index 8b77e430650c9..ebc6b55ad2896 100644 --- a/frontend/src/scenes/funnels/FunnelCanvasLabel.tsx +++ b/frontend/src/scenes/funnels/FunnelCanvasLabel.tsx @@ -1,16 +1,16 @@ -import React from 'react' -import { useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelDataLogic } from './funnelDataLogic' - import { Link } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { useActions, useValues } from 'kea' import { IconInfo } from 'lib/lemon-ui/icons' -import { FunnelVizType } from '~/types' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { humanFriendlyDuration, percentage } from 'lib/utils' +import React from 'react' +import { insightLogic } from 'scenes/insights/insightLogic' import { FunnelStepsPicker } from 'scenes/insights/views/Funnels/FunnelStepsPicker' +import { FunnelVizType } from '~/types' + +import { funnelDataLogic } from './funnelDataLogic' + export function FunnelCanvasLabel(): JSX.Element | null { const { insightProps } = useValues(insightLogic) const { conversionMetrics, aggregationTargetLabel, funnelsFilter } = useValues(funnelDataLogic(insightProps)) diff --git a/frontend/src/scenes/funnels/FunnelHistogram.tsx b/frontend/src/scenes/funnels/FunnelHistogram.tsx index 183db4a358fd9..0c1c95ebbc80f 100644 --- a/frontend/src/scenes/funnels/FunnelHistogram.tsx +++ b/frontend/src/scenes/funnels/FunnelHistogram.tsx @@ -1,11 +1,13 @@ -import { useRef } from 'react' -import { useValues } from 'kea' -import clsx from 'clsx' +import './FunnelHistogram.scss' + import useSize from '@react-hook/size' +import clsx from 'clsx' +import { useValues } from 'kea' import { hashCodeForString, humanFriendlyDuration } from 'lib/utils' -import { Histogram } from 'scenes/insights/views/Histogram' +import { useRef } from 'react' import { insightLogic } from 'scenes/insights/insightLogic' -import './FunnelHistogram.scss' +import { Histogram } from 'scenes/insights/views/Histogram' + import { funnelDataLogic } from './funnelDataLogic' export function FunnelHistogram(): JSX.Element | null { diff --git a/frontend/src/scenes/funnels/FunnelLineGraph.tsx b/frontend/src/scenes/funnels/FunnelLineGraph.tsx index 4b1efe973b922..d5b7bf0dea8f8 100644 --- a/frontend/src/scenes/funnels/FunnelLineGraph.tsx +++ b/frontend/src/scenes/funnels/FunnelLineGraph.tsx @@ -1,16 +1,18 @@ -import { LineGraph } from 'scenes/insights/views/LineGraph/LineGraph' -import { ChartParams, GraphType, GraphDataset } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { capitalizeFirstLetter, shortTimeZone } from 'lib/utils' +import { useValues } from 'kea' import { dayjs } from 'lib/dayjs' +import { capitalizeFirstLetter, shortTimeZone } from 'lib/utils' +import { insightLogic } from 'scenes/insights/insightLogic' import { getFormattedDate } from 'scenes/insights/InsightTooltip/insightTooltipUtils' -import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' +import { LineGraph } from 'scenes/insights/views/LineGraph/LineGraph' import { buildPeopleUrl } from 'scenes/trends/persons-modal/persons-modal-utils' -import { useValues } from 'kea' -import { funnelDataLogic } from './funnelDataLogic' +import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' + import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { isInsightQueryNode } from '~/queries/utils' import { TrendsFilter } from '~/queries/schema' +import { isInsightQueryNode } from '~/queries/utils' +import { ChartParams, GraphDataset, GraphType } from '~/types' + +import { funnelDataLogic } from './funnelDataLogic' const LineGraphWrapper = ({ inCardView, children }: { inCardView?: boolean; children: JSX.Element }): JSX.Element => { if (inCardView) { diff --git a/frontend/src/scenes/funnels/FunnelStepMore.tsx b/frontend/src/scenes/funnels/FunnelStepMore.tsx index 0381afb70ad7c..8f62437b3a619 100644 --- a/frontend/src/scenes/funnels/FunnelStepMore.tsx +++ b/frontend/src/scenes/funnels/FunnelStepMore.tsx @@ -1,12 +1,14 @@ -import { FunnelPathType, PathType, InsightType } from '~/types' import { useValues } from 'kea' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' import { insightLogic } from 'scenes/insights/insightLogic' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' import { urls } from 'scenes/urls' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { funnelDataLogic } from './funnelDataLogic' + import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { FunnelPathType, InsightType, PathType } from '~/types' + +import { funnelDataLogic } from './funnelDataLogic' type FunnelStepMoreProps = { stepIndex: number diff --git a/frontend/src/scenes/funnels/ValueInspectorButton.tsx b/frontend/src/scenes/funnels/ValueInspectorButton.tsx index ab11458287034..2aa8f29aaef22 100644 --- a/frontend/src/scenes/funnels/ValueInspectorButton.tsx +++ b/frontend/src/scenes/funnels/ValueInspectorButton.tsx @@ -1,6 +1,5 @@ -import { forwardRef } from 'react' - import { Link } from '@posthog/lemon-ui' +import { forwardRef } from 'react' interface ValueInspectorButtonProps { onClick?: (e?: React.MouseEvent) => void diff --git a/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.test.ts b/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.test.ts index 304dbc0b2e2cc..9d807251d6761 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.test.ts @@ -1,11 +1,11 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { DataNode } from '~/queries/schema' +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' +import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' +import { DataNode } from '~/queries/schema' +import { initKeaTests } from '~/test/init' import { FunnelCorrelationResultsType, FunnelCorrelationType, InsightLogicProps, InsightType } from '~/types' -import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { funnelCorrelationDetailsLogic } from './funnelCorrelationDetailsLogic' const funnelResults = [ diff --git a/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.ts b/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.ts index 1fd130da21e74..ba55207cb746f 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationDetailsLogic.ts @@ -1,10 +1,10 @@ -import { kea, props, key, path, connect, selectors, reducers, actions } from 'kea' +import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { FunnelCorrelation, InsightLogicProps } from '~/types' -import { funnelDataLogic } from './funnelDataLogic' +import { FunnelCorrelation, InsightLogicProps } from '~/types' import type { funnelCorrelationDetailsLogicType } from './funnelCorrelationDetailsLogicType' +import { funnelDataLogic } from './funnelDataLogic' export const funnelCorrelationDetailsLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.test.ts b/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.test.ts index 58880d9167c04..b88946646be5c 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.test.ts @@ -1,9 +1,11 @@ -import posthog from 'posthog-js' import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { AvailableFeature, InsightLogicProps, InsightType } from '~/types' +import posthog from 'posthog-js' + import { useAvailableFeatures } from '~/mocks/features' +import { initKeaTests } from '~/test/init' +import { AvailableFeature, InsightLogicProps, InsightType } from '~/types' + import { funnelCorrelationFeedbackLogic } from './funnelCorrelationFeedbackLogic' describe('funnelCorrelationFeedbackLogic', () => { diff --git a/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.ts b/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.ts index 14d89ea0b8f42..3277c70423541 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationFeedbackLogic.ts @@ -1,10 +1,11 @@ -import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea' import { lemonToast } from '@posthog/lemon-ui' +import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import type { funnelCorrelationFeedbackLogicType } from './funnelCorrelationFeedbackLogicType' import { InsightLogicProps } from '~/types' + +import type { funnelCorrelationFeedbackLogicType } from './funnelCorrelationFeedbackLogicType' import { funnelCorrelationLogic } from './funnelCorrelationLogic' import { funnelPropertyCorrelationLogic } from './funnelPropertyCorrelationLogic' diff --git a/frontend/src/scenes/funnels/funnelCorrelationLogic.ts b/frontend/src/scenes/funnels/funnelCorrelationLogic.ts index dad42e10f64b6..565be21a9d5e3 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationLogic.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationLogic.ts @@ -1,4 +1,12 @@ -import { kea, props, key, path, selectors, listeners, connect, reducers, actions, defaults } from 'kea' +import { lemonToast } from '@posthog/lemon-ui' +import { actions, connect, defaults, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import api from 'lib/api' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { teamLogic } from 'scenes/teamLogic' + +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { FunnelCorrelation, FunnelCorrelationResultsType, @@ -6,16 +14,9 @@ import { FunnelsFilterType, InsightLogicProps, } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import api from 'lib/api' import type { funnelCorrelationLogicType } from './funnelCorrelationLogicType' -import { loaders } from 'kea-loaders' -import { lemonToast } from '@posthog/lemon-ui' -import { teamLogic } from 'scenes/teamLogic' import { funnelDataLogic } from './funnelDataLogic' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { appendToCorrelationConfig } from './funnelUtils' export const funnelCorrelationLogic = kea([ diff --git a/frontend/src/scenes/funnels/funnelCorrelationUsageLogic.ts b/frontend/src/scenes/funnels/funnelCorrelationUsageLogic.ts index baf28ae1fc156..d2bfb449a52b2 100644 --- a/frontend/src/scenes/funnels/funnelCorrelationUsageLogic.ts +++ b/frontend/src/scenes/funnels/funnelCorrelationUsageLogic.ts @@ -1,18 +1,18 @@ import { BreakPointFunction, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { visibilitySensorLogic } from 'lib/components/VisibilitySensor/visibilitySensorLogic' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { EntityTypes, FunnelCorrelationResultsType, FunnelsFilterType, InsightLogicProps } from '~/types' -import { visibilitySensorLogic } from 'lib/components/VisibilitySensor/visibilitySensorLogic' +import { funnelCorrelationLogic } from './funnelCorrelationLogic' import type { funnelCorrelationUsageLogicType } from './funnelCorrelationUsageLogicType' -import { insightLogic } from 'scenes/insights/insightLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { parseEventAndProperty } from './funnelUtils' -import { funnelPersonsModalLogic } from './funnelPersonsModalLogic' import { funnelDataLogic } from './funnelDataLogic' -import { funnelCorrelationLogic } from './funnelCorrelationLogic' +import { funnelPersonsModalLogic } from './funnelPersonsModalLogic' import { funnelPropertyCorrelationLogic } from './funnelPropertyCorrelationLogic' +import { parseEventAndProperty } from './funnelUtils' export const funnelCorrelationUsageLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/scenes/funnels/funnelDataLogic.test.ts b/frontend/src/scenes/funnels/funnelDataLogic.test.ts index 1b48816206ee3..a2d90bfc81ee2 100644 --- a/frontend/src/scenes/funnels/funnelDataLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelDataLogic.test.ts @@ -1,13 +1,12 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { teamLogic } from 'scenes/teamLogic' import timekeeper from 'timekeeper' -import { teamLogic } from 'scenes/teamLogic' import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' -import { funnelDataLogic } from './funnelDataLogic' - -import { FunnelConversionWindowTimeUnit, FunnelVizType, InsightLogicProps, InsightModel, InsightType } from '~/types' import { DataNode, FunnelsQuery, NodeKind } from '~/queries/schema' +import { initKeaTests } from '~/test/init' +import { FunnelConversionWindowTimeUnit, FunnelVizType, InsightLogicProps, InsightModel, InsightType } from '~/types' + import { funnelResult, funnelResultTimeToConvert, @@ -16,6 +15,7 @@ import { funnelResultWithBreakdown, funnelResultWithMultiBreakdown, } from './__mocks__/funnelDataLogicMocks' +import { funnelDataLogic } from './funnelDataLogic' let logic: ReturnType let builtDataNodeLogic: ReturnType diff --git a/frontend/src/scenes/funnels/funnelDataLogic.ts b/frontend/src/scenes/funnels/funnelDataLogic.ts index 16b0c8e35924d..944ed7501434f 100644 --- a/frontend/src/scenes/funnels/funnelDataLogic.ts +++ b/frontend/src/scenes/funnels/funnelDataLogic.ts @@ -1,29 +1,32 @@ -import { kea, path, props, key, connect, selectors, actions, reducers } from 'kea' +import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea' +import { BIN_COUNT_AUTO } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { average, percentage, sum } from 'lib/utils' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { groupsModel, Noun } from '~/models/groupsModel' +import { NodeKind } from '~/queries/schema' +import { isFunnelsQuery } from '~/queries/utils' import { + FlattenedFunnelStepByBreakdown, + FunnelAPIResponse, + FunnelConversionWindow, + FunnelConversionWindowTimeUnit, FunnelResultType, - FunnelVizType, FunnelStepReference, - FunnelStepWithNestedBreakdown, - InsightLogicProps, - StepOrderValue, FunnelStepWithConversionMetrics, - FlattenedFunnelStepByBreakdown, + FunnelStepWithNestedBreakdown, FunnelsTimeConversionBins, - HistogramGraphDatum, - FunnelAPIResponse, FunnelTimeConversionMetrics, + FunnelVizType, + HistogramGraphDatum, + InsightLogicProps, + StepOrderValue, TrendResult, - FunnelConversionWindowTimeUnit, - FunnelConversionWindow, } from '~/types' -import { NodeKind } from '~/queries/schema' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { groupsModel, Noun } from '~/models/groupsModel' import type { funnelDataLogicType } from './funnelDataLogicType' -import { isFunnelsQuery } from '~/queries/utils' -import { percentage, sum, average } from 'lib/utils' -import { dayjs } from 'lib/dayjs' import { aggregateBreakdownResult, aggregationLabelForHogQL, @@ -35,8 +38,6 @@ import { isBreakdownFunnelResults, stepsWithConversionMetrics, } from './funnelUtils' -import { BIN_COUNT_AUTO } from 'lib/constants' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' const DEFAULT_FUNNEL_LOGIC_KEY = 'default_funnel_key' diff --git a/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts b/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts index f99fca6e37db1..a15b9f5466010 100644 --- a/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelPersonsModalLogic.test.ts @@ -1,15 +1,15 @@ -import { expectLogic } from 'kea-test-utils' import { router } from 'kea-router' -import { initKeaTests } from '~/test/init' - -import { funnelPersonsModalLogic } from './funnelPersonsModalLogic' -import { teamLogic } from 'scenes/teamLogic' +import { expectLogic } from 'kea-test-utils' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' - -import { InsightLogicProps, InsightShortId, InsightType } from '~/types' +import { teamLogic } from 'scenes/teamLogic' +import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' import { urls } from 'scenes/urls' + import { useMocks } from '~/mocks/jest' -import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' +import { initKeaTests } from '~/test/init' +import { InsightLogicProps, InsightShortId, InsightType } from '~/types' + +import { funnelPersonsModalLogic } from './funnelPersonsModalLogic' jest.mock('scenes/trends/persons-modal/PersonsModal') diff --git a/frontend/src/scenes/funnels/funnelPersonsModalLogic.ts b/frontend/src/scenes/funnels/funnelPersonsModalLogic.ts index 242ebe4a6d343..d9f6c286d8043 100644 --- a/frontend/src/scenes/funnels/funnelPersonsModalLogic.ts +++ b/frontend/src/scenes/funnels/funnelPersonsModalLogic.ts @@ -1,5 +1,9 @@ import { actions, connect, kea, key, listeners, path, props, selectors } from 'kea' +import { insightLogic } from 'scenes/insights/insightLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { funnelTitle } from 'scenes/trends/persons-modal/persons-modal-utils' +import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' + import { FunnelCorrelation, FunnelCorrelationResultsType, @@ -8,19 +12,14 @@ import { InsightLogicProps, } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelDataLogic } from './funnelDataLogic' - +import type { funnelPersonsModalLogicType } from './funnelPersonsModalLogicType' import { - getBreakdownStepValues, generateBaselineConversionUrl, + getBreakdownStepValues, parseBreakdownValue, parseEventAndProperty, } from './funnelUtils' -import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' -import { funnelTitle } from 'scenes/trends/persons-modal/persons-modal-utils' - -import type { funnelPersonsModalLogicType } from './funnelPersonsModalLogicType' const DEFAULT_FUNNEL_LOGIC_KEY = 'default_funnel_key' diff --git a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts index c9e3b8ae77be1..731ef82dba5f3 100644 --- a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts +++ b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.test.ts @@ -2,10 +2,12 @@ import { expectLogic, partial } from 'kea-test-utils' import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' + import { useAvailableFeatures } from '~/mocks/features' import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' import { AvailableFeature, CorrelationConfigType, InsightLogicProps, InsightShortId, InsightType } from '~/types' + import { DEFAULT_EXCLUDED_PERSON_PROPERTIES, funnelPropertyCorrelationLogic } from './funnelPropertyCorrelationLogic' const Insight123 = '123' as InsightShortId diff --git a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.ts b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.ts index 46a207dbbd290..f611b88510fc7 100644 --- a/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.ts +++ b/frontend/src/scenes/funnels/funnelPropertyCorrelationLogic.ts @@ -1,17 +1,16 @@ -import { kea, props, key, path, selectors, listeners, connect, reducers, actions, defaults } from 'kea' +import { lemonToast } from '@posthog/lemon-ui' +import { actions, connect, defaults, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' +import api from 'lib/api' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { teamLogic } from '../teamLogic' import { groupPropertiesModel } from '~/models/groupPropertiesModel' - import { FunnelCorrelation, FunnelCorrelationResultsType, FunnelCorrelationType, InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { appendToCorrelationConfig } from './funnelUtils' -import api from 'lib/api' -import { lemonToast } from '@posthog/lemon-ui' -import type { funnelPropertyCorrelationLogicType } from './funnelPropertyCorrelationLogicType' +import { teamLogic } from '../teamLogic' import { funnelCorrelationLogic } from './funnelCorrelationLogic' +import type { funnelPropertyCorrelationLogicType } from './funnelPropertyCorrelationLogicType' +import { appendToCorrelationConfig } from './funnelUtils' // List of events that should be excluded, if we don't have an explicit list of // excluded properties. Copied from diff --git a/frontend/src/scenes/funnels/funnelTooltipLogic.ts b/frontend/src/scenes/funnels/funnelTooltipLogic.ts index e2c09db2705bd..3c29a96f581cf 100644 --- a/frontend/src/scenes/funnels/funnelTooltipLogic.ts +++ b/frontend/src/scenes/funnels/funnelTooltipLogic.ts @@ -1,4 +1,4 @@ -import { actions, kea, reducers, path, props, key } from 'kea' +import { actions, kea, key, path, props, reducers } from 'kea' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { FunnelStepWithConversionMetrics, InsightLogicProps } from '~/types' diff --git a/frontend/src/scenes/funnels/funnelUtils.test.ts b/frontend/src/scenes/funnels/funnelUtils.test.ts index fac6a2b82f0cd..d47b7e171a39d 100644 --- a/frontend/src/scenes/funnels/funnelUtils.test.ts +++ b/frontend/src/scenes/funnels/funnelUtils.test.ts @@ -1,12 +1,5 @@ -import { - EMPTY_BREAKDOWN_VALUES, - getBreakdownStepValues, - getIncompleteConversionWindowStartDate, - getMeanAndStandardDeviation, - getClampedStepRangeFilter, - getVisibilityKey, - parseDisplayNameForCorrelation, -} from './funnelUtils' +import { dayjs } from 'lib/dayjs' + import { FilterType, FunnelConversionWindowTimeUnit, @@ -15,7 +8,16 @@ import { FunnelCorrelationType, FunnelExclusion, } from '~/types' -import { dayjs } from 'lib/dayjs' + +import { + EMPTY_BREAKDOWN_VALUES, + getBreakdownStepValues, + getClampedStepRangeFilter, + getIncompleteConversionWindowStartDate, + getMeanAndStandardDeviation, + getVisibilityKey, + parseDisplayNameForCorrelation, +} from './funnelUtils' describe('getMeanAndStandardDeviation', () => { const arrayToExpectedValues: [number[], number[]][] = [ diff --git a/frontend/src/scenes/funnels/funnelUtils.ts b/frontend/src/scenes/funnels/funnelUtils.ts index dcfd68f91470f..b19dcd865fed2 100644 --- a/frontend/src/scenes/funnels/funnelUtils.ts +++ b/frontend/src/scenes/funnels/funnelUtils.ts @@ -1,31 +1,32 @@ +import { combineUrl } from 'kea-router' +import { FunnelLayout } from 'lib/constants' +import { dayjs } from 'lib/dayjs' import { autoCaptureEventToDescription, clamp } from 'lib/utils' +import { elementsToAction } from 'scenes/events/createActionFromEvent' +import { teamLogic } from 'scenes/teamLogic' + +import { Noun } from '~/models/groupsModel' +import { FunnelsQuery } from '~/queries/schema' import { - FunnelExclusion, - FunnelStep, - FunnelStepWithNestedBreakdown, + AnyPropertyFilter, + Breakdown, BreakdownKeyType, - FunnelResultType, - FunnelStepReference, + CorrelationConfigType, + ElementPropertyFilter, + FlattenedFunnelStepByBreakdown, FunnelConversionWindow, + FunnelCorrelation, + FunnelCorrelationResultsType, + FunnelExclusion, + FunnelResultType, FunnelsFilterType, - Breakdown, + FunnelStep, + FunnelStepReference, FunnelStepWithConversionMetrics, - FlattenedFunnelStepByBreakdown, - FunnelCorrelation, - AnyPropertyFilter, - PropertyOperator, - ElementPropertyFilter, + FunnelStepWithNestedBreakdown, PropertyFilterType, - FunnelCorrelationResultsType, - CorrelationConfigType, + PropertyOperator, } from '~/types' -import { dayjs } from 'lib/dayjs' -import { combineUrl } from 'kea-router' -import { FunnelsQuery } from '~/queries/schema' -import { FunnelLayout } from 'lib/constants' -import { elementsToAction } from 'scenes/events/createActionFromEvent' -import { teamLogic } from 'scenes/teamLogic' -import { Noun } from '~/models/groupsModel' /** Chosen via heuristics by eyeballing some values * Assuming a normal distribution, then 90% of values are within 1.5 standard deviations of the mean diff --git a/frontend/src/scenes/funnels/useFunnelTooltip.tsx b/frontend/src/scenes/funnels/useFunnelTooltip.tsx index c0d311e6b7361..d6faa42f4e8ac 100644 --- a/frontend/src/scenes/funnels/useFunnelTooltip.tsx +++ b/frontend/src/scenes/funnels/useFunnelTooltip.tsx @@ -1,21 +1,23 @@ import { useValues } from 'kea' -import { useEffect, useRef } from 'react' -import { FunnelStepWithConversionMetrics } from '~/types' +import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' import { LemonRow } from 'lib/lemon-ui/LemonRow' import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' -import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' -import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' import { humanFriendlyDuration, humanFriendlyNumber, percentage } from 'lib/utils' +import { useEffect, useRef } from 'react' +import { insightLogic } from 'scenes/insights/insightLogic' +import { ClickToInspectActors } from 'scenes/insights/InsightTooltip/InsightTooltip' +import { formatBreakdownLabel } from 'scenes/insights/utils' +import { getActionFilterFromFunnelStep } from 'scenes/insights/views/Funnels/funnelStepTableUtils' import { ensureTooltip } from 'scenes/insights/views/LineGraph/LineGraph' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' + import { cohortsModel } from '~/models/cohortsModel' -import { ClickToInspectActors } from 'scenes/insights/InsightTooltip/InsightTooltip' import { groupsModel } from '~/models/groupsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatBreakdownLabel } from 'scenes/insights/utils' import { BreakdownFilter } from '~/queries/schema' +import { FunnelStepWithConversionMetrics } from '~/types' + import { funnelDataLogic } from './funnelDataLogic' -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelTooltipLogic } from './funnelTooltipLogic' /** The tooltip is offset horizontally by a few pixels from the bar to give it some breathing room. */ diff --git a/frontend/src/scenes/groups/Group.tsx b/frontend/src/scenes/groups/Group.tsx index 21c53e74133f4..41fc57fcb6b03 100644 --- a/frontend/src/scenes/groups/Group.tsx +++ b/frontend/src/scenes/groups/Group.tsx @@ -1,22 +1,23 @@ import { useActions, useValues } from 'kea' +import { router } from 'kea-router' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { NotFound } from 'lib/components/NotFound' +import { PageHeader } from 'lib/components/PageHeader' import { PropertiesTable } from 'lib/components/PropertiesTable' import { TZLabel } from 'lib/components/TZLabel' -import { GroupLogicProps, groupLogic } from 'scenes/groups/groupLogic' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Spinner, SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { GroupDashboard } from 'scenes/groups/GroupDashboard' +import { groupLogic, GroupLogicProps } from 'scenes/groups/groupLogic' import { RelatedGroups } from 'scenes/groups/RelatedGroups' -import { SceneExport } from 'scenes/sceneTypes' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { Group as IGroup, NotebookNodeType, PersonsTabType, PropertyDefinitionType } from '~/types' -import { PageHeader } from 'lib/components/PageHeader' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { Spinner, SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { NotFound } from 'lib/components/NotFound' import { RelatedFeatureFlags } from 'scenes/persons/RelatedFeatureFlags' -import { Query } from '~/queries/Query/Query' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { GroupDashboard } from 'scenes/groups/GroupDashboard' -import { router } from 'kea-router' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' + +import { Query } from '~/queries/Query/Query' +import { Group as IGroup, NotebookNodeType, PersonsTabType, PropertyDefinitionType } from '~/types' interface GroupSceneProps { groupTypeIndex?: string diff --git a/frontend/src/scenes/groups/GroupDashboard.tsx b/frontend/src/scenes/groups/GroupDashboard.tsx index aaf4bd166d7c9..4f38d22b367cf 100644 --- a/frontend/src/scenes/groups/GroupDashboard.tsx +++ b/frontend/src/scenes/groups/GroupDashboard.tsx @@ -1,14 +1,15 @@ -import { useActions, useValues } from 'kea' -import { Dashboard } from 'scenes/dashboard/Dashboard' -import { Scene } from 'scenes/sceneTypes' -import { Group, GroupPropertyFilter, PropertyFilterType, PropertyOperator } from '~/types' -import { groupDashboardLogic } from 'scenes/groups/groupDashboardLogic' import { LemonButton } from '@posthog/lemon-ui' -import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' +import { useActions, useValues } from 'kea' import { SceneDashboardChoiceModal } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceModal' import { sceneDashboardChoiceModalLogic } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' import { useEffect } from 'react' +import { Dashboard } from 'scenes/dashboard/Dashboard' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { groupDashboardLogic } from 'scenes/groups/groupDashboardLogic' +import { Scene } from 'scenes/sceneTypes' + +import { Group, GroupPropertyFilter, PropertyFilterType, PropertyOperator } from '~/types' export function GroupDashboard({ groupData }: { groupData: Group }): JSX.Element { const { showSceneDashboardChoiceModal } = useActions(sceneDashboardChoiceModalLogic({ scene: Scene.Group })) diff --git a/frontend/src/scenes/groups/Groups.tsx b/frontend/src/scenes/groups/Groups.tsx index ec50e9950543c..c84835d24ac7a 100644 --- a/frontend/src/scenes/groups/Groups.tsx +++ b/frontend/src/scenes/groups/Groups.tsx @@ -1,20 +1,22 @@ import { useActions, useValues } from 'kea' -import { Group, PropertyDefinitionType } from '~/types' -import { groupsListLogic } from './groupsListLogic' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { PropertiesTable } from 'lib/components/PropertiesTable' -import { LemonTableColumns } from 'lib/lemon-ui/LemonTable/types' import { TZLabel } from 'lib/components/TZLabel' -import { LemonTable } from 'lib/lemon-ui/LemonTable' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' -import { GroupsIntroduction } from 'scenes/groups/GroupsIntroduction' import { groupsAccessLogic, GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' -import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonTable } from 'lib/lemon-ui/LemonTable' +import { LemonTableColumns } from 'lib/lemon-ui/LemonTable/types' +import { Link } from 'lib/lemon-ui/Link' import { capitalizeFirstLetter } from 'lib/utils' +import { GroupsIntroduction } from 'scenes/groups/GroupsIntroduction' +import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { urls } from 'scenes/urls' + +import { Group, PropertyDefinitionType } from '~/types' + +import { groupsListLogic } from './groupsListLogic' export function Groups({ groupTypeIndex }: { groupTypeIndex: number }): JSX.Element { const { diff --git a/frontend/src/scenes/groups/GroupsIntroduction.tsx b/frontend/src/scenes/groups/GroupsIntroduction.tsx index dde05b7e91c94..eb2bda5801ff9 100644 --- a/frontend/src/scenes/groups/GroupsIntroduction.tsx +++ b/frontend/src/scenes/groups/GroupsIntroduction.tsx @@ -1,7 +1,8 @@ -import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' +import { Link } from '@posthog/lemon-ui' import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' +import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' + import { AvailableFeature } from '~/types' -import { Link } from '@posthog/lemon-ui' interface Props { access: GroupsAccessStatus.NoAccess | GroupsAccessStatus.HasAccess | GroupsAccessStatus.HasGroupTypes diff --git a/frontend/src/scenes/groups/RelatedGroups.tsx b/frontend/src/scenes/groups/RelatedGroups.tsx index 396ff4150be9f..c82ee3488aa33 100644 --- a/frontend/src/scenes/groups/RelatedGroups.tsx +++ b/frontend/src/scenes/groups/RelatedGroups.tsx @@ -1,12 +1,13 @@ +import { IconPerson } from '@posthog/icons' import { useValues } from 'kea' import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { ActorType } from '~/types' -import { groupsModel } from '~/models/groupsModel' import { capitalizeFirstLetter } from 'lib/utils' -import { PersonDisplay } from 'scenes/persons/PersonDisplay' import { relatedGroupsLogic } from 'scenes/groups/relatedGroupsLogic' import { GroupActorDisplay } from 'scenes/persons/GroupActorDisplay' -import { IconPerson } from '@posthog/icons' +import { PersonDisplay } from 'scenes/persons/PersonDisplay' + +import { groupsModel } from '~/models/groupsModel' +import { ActorType } from '~/types' interface Props { groupTypeIndex: number | null diff --git a/frontend/src/scenes/groups/groupDashboardLogic.ts b/frontend/src/scenes/groups/groupDashboardLogic.ts index 01c46d05946ae..1714c529365ab 100644 --- a/frontend/src/scenes/groups/groupDashboardLogic.ts +++ b/frontend/src/scenes/groups/groupDashboardLogic.ts @@ -1,11 +1,11 @@ -import { connect, kea, selectors, path } from 'kea' +import { connect, kea, path, selectors } from 'kea' +import { DashboardLogicProps } from 'scenes/dashboard/dashboardLogic' +import { Scene } from 'scenes/sceneTypes' +import { userLogic } from 'scenes/userLogic' import { DashboardPlacement } from '~/types' -import { Scene } from 'scenes/sceneTypes' import type { groupDashboardLogicType } from './groupDashboardLogicType' -import { DashboardLogicProps } from 'scenes/dashboard/dashboardLogic' -import { userLogic } from 'scenes/userLogic' export const groupDashboardLogic = kea([ path(['scenes', 'groups', 'groupDashboardLogic']), diff --git a/frontend/src/scenes/groups/groupLogic.ts b/frontend/src/scenes/groups/groupLogic.ts index bbdc071690c73..5fa0ab912d69f 100644 --- a/frontend/src/scenes/groups/groupLogic.ts +++ b/frontend/src/scenes/groups/groupLogic.ts @@ -1,21 +1,23 @@ import { actions, afterMount, connect, kea, key, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { urlToAction } from 'kea-router' import api from 'lib/api' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { toParams } from 'lib/utils' -import { teamLogic } from 'scenes/teamLogic' -import { groupsModel } from '~/models/groupsModel' -import { Breadcrumb, Group, GroupTypeIndex, PropertyFilterType, PropertyOperator } from '~/types' -import type { groupLogicType } from './groupLogicType' -import { urls } from 'scenes/urls' import { capitalizeFirstLetter } from 'lib/utils' import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { DataTableNode, Node, NodeKind } from '~/queries/schema' +import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { groupsModel } from '~/models/groupsModel' import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' +import { DataTableNode, Node, NodeKind } from '~/queries/schema' import { isDataTableNode } from '~/queries/utils' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { loaders } from 'kea-loaders' -import { urlToAction } from 'kea-router' -import { Scene } from 'scenes/sceneTypes' +import { Breadcrumb, Group, GroupTypeIndex, PropertyFilterType, PropertyOperator } from '~/types' + +import type { groupLogicType } from './groupLogicType' function getGroupEventsQuery(groupTypeIndex: number, groupKey: string): DataTableNode { return { diff --git a/frontend/src/scenes/groups/groupsListLogic.ts b/frontend/src/scenes/groups/groupsListLogic.ts index 639aecd2ee007..212545077ae10 100644 --- a/frontend/src/scenes/groups/groupsListLogic.ts +++ b/frontend/src/scenes/groups/groupsListLogic.ts @@ -1,10 +1,12 @@ +import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, afterMount } from 'kea' import api from 'lib/api' import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' import { teamLogic } from 'scenes/teamLogic' -import { Noun, groupsModel } from '~/models/groupsModel' + +import { groupsModel, Noun } from '~/models/groupsModel' import { Group } from '~/types' + import type { groupsListLogicType } from './groupsListLogicType' export interface GroupsPaginatedResponse { diff --git a/frontend/src/scenes/groups/relatedGroupsLogic.ts b/frontend/src/scenes/groups/relatedGroupsLogic.ts index 60c9d224d80be..a5088d95ddd3e 100644 --- a/frontend/src/scenes/groups/relatedGroupsLogic.ts +++ b/frontend/src/scenes/groups/relatedGroupsLogic.ts @@ -1,9 +1,11 @@ +import { actions, connect, events, kea, key, path, props } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, events } from 'kea' import api from 'lib/api' import { toParams } from 'lib/utils' import { teamLogic } from 'scenes/teamLogic' + import { ActorType } from '~/types' + import type { relatedGroupsLogicType } from './relatedGroupsLogicType' export const relatedGroupsLogic = kea([ diff --git a/frontend/src/scenes/insights/EditorFilters/AttributionFilter.tsx b/frontend/src/scenes/insights/EditorFilters/AttributionFilter.tsx index 3aff4375e8c36..013ca006ca8d4 100644 --- a/frontend/src/scenes/insights/EditorFilters/AttributionFilter.tsx +++ b/frontend/src/scenes/insights/EditorFilters/AttributionFilter.tsx @@ -1,8 +1,10 @@ -import { useActions, useValues } from 'kea' -import { BreakdownAttributionType, EditorFilterProps, StepOrderValue } from '~/types' import { LemonSelect } from '@posthog/lemon-ui' -import { FunnelsFilter } from '~/queries/schema' +import { useActions, useValues } from 'kea' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' + +import { FunnelsFilter } from '~/queries/schema' +import { BreakdownAttributionType, EditorFilterProps, StepOrderValue } from '~/types' + import { FUNNEL_STEP_COUNT_LIMIT } from './FunnelsQuerySteps' export function Attribution({ insightProps }: EditorFilterProps): JSX.Element { diff --git a/frontend/src/scenes/insights/EditorFilters/FunnelsAdvanced.tsx b/frontend/src/scenes/insights/EditorFilters/FunnelsAdvanced.tsx index 8443b7e432f9d..b7ad15cc28546 100644 --- a/frontend/src/scenes/insights/EditorFilters/FunnelsAdvanced.tsx +++ b/frontend/src/scenes/insights/EditorFilters/FunnelsAdvanced.tsx @@ -1,12 +1,14 @@ -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' +import { PureField } from 'lib/forms/Field' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' + +import { Noun } from '~/models/groupsModel' import { EditorFilterProps } from '~/types' -import { FunnelStepOrderPicker } from '../views/Funnels/FunnelStepOrderPicker' + import { FunnelExclusionsFilter } from '../filters/FunnelExclusionsFilter/FunnelExclusionsFilter' import { FunnelStepReferencePicker } from '../filters/FunnelStepReferencePicker' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { PureField } from 'lib/forms/Field' -import { Noun } from '~/models/groupsModel' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { FunnelStepOrderPicker } from '../views/Funnels/FunnelStepOrderPicker' export function FunnelsAdvanced({ insightProps }: EditorFilterProps): JSX.Element { const { querySource, aggregationTargetLabel, advancedOptionsUsedCount } = useValues(funnelDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx b/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx index 8b0cf093c12c3..265dbfa4267c7 100644 --- a/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx +++ b/frontend/src/scenes/insights/EditorFilters/FunnelsQuerySteps.tsx @@ -1,20 +1,21 @@ -import { useValues, useActions } from 'kea' -import { groupsModel } from '~/models/groupsModel' - -import { FilterType, EditorFilterProps } from '~/types' -import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' +import { useActions, useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { FunnelVizType } from '../views/Funnels/FunnelVizType' -import { ActionFilter } from '../filters/ActionFilter/ActionFilter' -import { AggregationSelect } from '../filters/AggregationSelect' -import { FunnelConversionWindowFilter } from '../views/Funnels/FunnelConversionWindowFilter' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { groupsModel } from '~/models/groupsModel' import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { FunnelsQuery } from '~/queries/schema' import { isInsightQueryNode } from '~/queries/utils' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { EditorFilterProps, FilterType } from '~/types' + +import { ActionFilter } from '../filters/ActionFilter/ActionFilter' +import { AggregationSelect } from '../filters/AggregationSelect' +import { FunnelConversionWindowFilter } from '../views/Funnels/FunnelConversionWindowFilter' +import { FunnelVizType } from '../views/Funnels/FunnelVizType' export const FUNNEL_STEP_COUNT_LIMIT = 20 diff --git a/frontend/src/scenes/insights/EditorFilters/PathsAdvanced.tsx b/frontend/src/scenes/insights/EditorFilters/PathsAdvanced.tsx index 150222f6a0013..624045ea8c957 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsAdvanced.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsAdvanced.tsx @@ -1,18 +1,17 @@ -import { useState } from 'react' -import { useActions, useValues } from 'kea' -import { InputNumber } from 'antd' - -import { AvailableFeature, PathEdgeParameters, EditorFilterProps } from '~/types' import { LemonDivider } from '@posthog/lemon-ui' +import { InputNumber } from 'antd' +import { useActions, useValues } from 'kea' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { IconSettings } from 'lib/lemon-ui/icons' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { Link } from 'lib/lemon-ui/Link' +import { useState } from 'react' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' +import { urls } from 'scenes/urls' -import { Link } from 'lib/lemon-ui/Link' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { IconSettings } from 'lib/lemon-ui/icons' +import { AvailableFeature, EditorFilterProps, PathEdgeParameters } from '~/types' import { PathCleaningFilter } from '../filters/PathCleaningFilter' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' -import { urls } from 'scenes/urls' export function PathsAdvanced({ insightProps, ...rest }: EditorFilterProps): JSX.Element { const { pathsFilter } = useValues(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/PathsEventTypes.tsx b/frontend/src/scenes/insights/EditorFilters/PathsEventTypes.tsx index abd05ea75767d..62a3e57f61732 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsEventTypes.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsEventTypes.tsx @@ -1,11 +1,13 @@ -import { useValues, useActions } from 'kea' -import { PathType, EditorFilterProps, PathsFilterType } from '~/types' -import { LemonButtonWithDropdown, LemonButton } from 'lib/lemon-ui/LemonButton' -import { humanizePathsEventTypes } from '../utils' +import { useActions, useValues } from 'kea' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' import { capitalizeFirstLetter } from 'lib/utils' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' +import { EditorFilterProps, PathsFilterType, PathType } from '~/types' + +import { humanizePathsEventTypes } from '../utils' + export function PathsEventsTypes({ insightProps }: EditorFilterProps): JSX.Element { const { pathsFilter } = useValues(pathsDataLogic(insightProps)) const { updateInsightFilter } = useActions(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/PathsExclusions.tsx b/frontend/src/scenes/insights/EditorFilters/PathsExclusions.tsx index 43b8f049772c1..9d5cf33daff62 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsExclusions.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsExclusions.tsx @@ -1,10 +1,9 @@ import { useActions, useValues } from 'kea' - -import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' - -import { EventPropertyFilter, PropertyFilterType, PropertyOperator, EditorFilterProps } from '~/types' import { PathItemFilters } from 'lib/components/PropertyFilters/PathItemFilters' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' + +import { EditorFilterProps, EventPropertyFilter, PropertyFilterType, PropertyOperator } from '~/types' export function PathsExclusions({ insightProps }: EditorFilterProps): JSX.Element { const { pathsFilter, taxonomicGroupTypes } = useValues(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/PathsHogQL.tsx b/frontend/src/scenes/insights/EditorFilters/PathsHogQL.tsx index a7ee22a7fa426..cfa09613faa6d 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsHogQL.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsHogQL.tsx @@ -1,9 +1,10 @@ -import { useValues, useActions } from 'kea' -import { EditorFilterProps } from '~/types' -import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' +import { useActions, useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { taxonomicEventFilterToHogQL } from '~/queries/utils' import { TaxonomicPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' +import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' + +import { taxonomicEventFilterToHogQL } from '~/queries/utils' +import { EditorFilterProps } from '~/types' export function PathsHogQL({ insightProps }: EditorFilterProps): JSX.Element { const { pathsFilter } = useValues(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/PathsTarget.tsx b/frontend/src/scenes/insights/EditorFilters/PathsTarget.tsx index 2e8ec22a4ab17..23e15741c1f24 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsTarget.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsTarget.tsx @@ -1,12 +1,11 @@ -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' import { combineUrl, encodeParams, router } from 'kea-router' - -import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' - -import { FunnelPathType, EditorFilterProps } from '~/types' import { PathItemSelector } from 'lib/components/PropertyFilters/components/PathItemSelector' -import { LemonButton, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' import { IconClose, IconFunnelVertical } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonWithSideAction } from 'lib/lemon-ui/LemonButton' +import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' + +import { EditorFilterProps, FunnelPathType } from '~/types' export function PathsTargetStart(props: EditorFilterProps): JSX.Element { return diff --git a/frontend/src/scenes/insights/EditorFilters/PathsWildcardGroups.tsx b/frontend/src/scenes/insights/EditorFilters/PathsWildcardGroups.tsx index 9682682262011..125abec183a42 100644 --- a/frontend/src/scenes/insights/EditorFilters/PathsWildcardGroups.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PathsWildcardGroups.tsx @@ -1,9 +1,8 @@ -import { useValues, useActions } from 'kea' - +import { useActions, useValues } from 'kea' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' import { EditorFilterProps } from '~/types' -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' export function PathsWildcardGroups({ insightProps }: EditorFilterProps): JSX.Element { const { pathsFilter } = useValues(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/PercentStackViewFilter.tsx b/frontend/src/scenes/insights/EditorFilters/PercentStackViewFilter.tsx index ac8a5a6c841da..9e31687ef2971 100644 --- a/frontend/src/scenes/insights/EditorFilters/PercentStackViewFilter.tsx +++ b/frontend/src/scenes/insights/EditorFilters/PercentStackViewFilter.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { insightLogic } from '../insightLogic' import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' +import { insightLogic } from '../insightLogic' + export function PercentStackViewFilter(): JSX.Element { const { insightProps } = useValues(insightLogic) const { showPercentStackView } = useValues(trendsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/RetentionSummary.tsx b/frontend/src/scenes/insights/EditorFilters/RetentionSummary.tsx index d8edd6e9e04a0..8859b225ef419 100644 --- a/frontend/src/scenes/insights/EditorFilters/RetentionSummary.tsx +++ b/frontend/src/scenes/insights/EditorFilters/RetentionSummary.tsx @@ -1,21 +1,23 @@ +import { IconInfo } from '@posthog/icons' +import { LemonInput, LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { Link } from 'lib/lemon-ui/Link' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { AggregationSelect } from 'scenes/insights/filters/AggregationSelect' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { dateOptionPlurals, dateOptions, retentionOptionDescriptions, retentionOptions, } from 'scenes/retention/constants' -import { FilterType, EditorFilterProps, RetentionType } from '~/types' -import { ActionFilter } from '../filters/ActionFilter/ActionFilter' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { AggregationSelect } from 'scenes/insights/filters/AggregationSelect' + import { groupsModel } from '~/models/groupsModel' +import { EditorFilterProps, FilterType, RetentionType } from '~/types' + +import { ActionFilter } from '../filters/ActionFilter/ActionFilter' import { MathAvailability } from '../filters/ActionFilter/ActionFilterRow/ActionFilterRow' -import { Link } from 'lib/lemon-ui/Link' -import { LemonInput, LemonSelect } from '@posthog/lemon-ui' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { IconInfo } from '@posthog/icons' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' export function RetentionSummary({ insightProps }: EditorFilterProps): JSX.Element { const { showGroupsOptions } = useValues(groupsModel) diff --git a/frontend/src/scenes/insights/EditorFilters/SamplingFilter.tsx b/frontend/src/scenes/insights/EditorFilters/SamplingFilter.tsx index 45bdd4b08491c..9a93aad12783a 100644 --- a/frontend/src/scenes/insights/EditorFilters/SamplingFilter.tsx +++ b/frontend/src/scenes/insights/EditorFilters/SamplingFilter.tsx @@ -1,9 +1,11 @@ -import { InsightLogicProps } from '~/types' import { LemonButton, LemonLabel, LemonSwitch, LemonTag } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { AVAILABLE_SAMPLING_PERCENTAGES, samplingFilterLogic } from './samplingFilterLogic' import posthog from 'posthog-js' +import { InsightLogicProps } from '~/types' + +import { AVAILABLE_SAMPLING_PERCENTAGES, samplingFilterLogic } from './samplingFilterLogic' + const DEFAULT_SAMPLING_INFO_TOOLTIP_CONTENT = 'Sampling computes the result on only a subset of the data, making insights load significantly faster.' diff --git a/frontend/src/scenes/insights/EditorFilters/ShowLegendFilter.tsx b/frontend/src/scenes/insights/EditorFilters/ShowLegendFilter.tsx index 0dc31ee402fd0..b6ae3a2a978fe 100644 --- a/frontend/src/scenes/insights/EditorFilters/ShowLegendFilter.tsx +++ b/frontend/src/scenes/insights/EditorFilters/ShowLegendFilter.tsx @@ -1,9 +1,8 @@ -import { useValues, useActions } from 'kea' - +import { LemonCheckbox } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { insightLogic } from 'scenes/insights/insightLogic' -import { insightVizDataLogic } from '../insightVizDataLogic' -import { LemonCheckbox } from '@posthog/lemon-ui' +import { insightVizDataLogic } from '../insightVizDataLogic' export function ShowLegendFilter(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/EditorFilters/ValueOnSeriesFilter.tsx b/frontend/src/scenes/insights/EditorFilters/ValueOnSeriesFilter.tsx index 37200a5dc3baf..142a2f1d89b4b 100644 --- a/frontend/src/scenes/insights/EditorFilters/ValueOnSeriesFilter.tsx +++ b/frontend/src/scenes/insights/EditorFilters/ValueOnSeriesFilter.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { insightLogic } from '../insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { insightLogic } from '../insightLogic' + export function ValueOnSeriesFilter(): JSX.Element { const { insightProps } = useValues(insightLogic) const { valueOnSeries } = useValues(insightVizDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/EditorFilters/samplingFilterLogic.ts b/frontend/src/scenes/insights/EditorFilters/samplingFilterLogic.ts index 2ffa3e6be974e..7c9ba8fbc3125 100644 --- a/frontend/src/scenes/insights/EditorFilters/samplingFilterLogic.ts +++ b/frontend/src/scenes/insights/EditorFilters/samplingFilterLogic.ts @@ -1,12 +1,11 @@ -import { kea, path, connect, actions, reducers, props, selectors, listeners, key } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { subscriptions } from 'kea-subscriptions' - -import { insightVizDataLogic } from '../insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { InsightLogicProps } from '~/types' +import { insightVizDataLogic } from '../insightVizDataLogic' import type { samplingFilterLogicType } from './samplingFilterLogicType' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' export const AVAILABLE_SAMPLING_PERCENTAGES = [0.1, 1, 10, 25] diff --git a/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx b/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx index ab3e7206c558f..d8eceb1eb429a 100644 --- a/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx +++ b/frontend/src/scenes/insights/EmptyStates/EmptyStates.stories.tsx @@ -1,21 +1,23 @@ -import { useEffect } from 'react' import { Meta, StoryObj } from '@storybook/react' -import funnelOneStep from './funnelOneStep.json' -import { useStorybookMocks } from '~/mocks/browser' import { router } from 'kea-router' -import insight from '../../../mocks/fixtures/api/projects/team_id/insights/trendsLine.json' -import { InsightShortId } from '~/types' -import { createInsightStory } from 'scenes/insights/__mocks__/createInsightScene' +import { useEffect } from 'react' import { App } from 'scenes/App' +import { createInsightStory } from 'scenes/insights/__mocks__/createInsightScene' + +import { useStorybookMocks } from '~/mocks/browser' +import { InsightShortId } from '~/types' + +import insight from '../../../mocks/fixtures/api/projects/team_id/insights/trendsLine.json' import { insightVizDataLogic } from '../insightVizDataLogic' +import funnelOneStep from './funnelOneStep.json' type Story = StoryObj const meta: Meta = { title: 'Scenes-App/Insights/Error states', + tags: ['test-skip'], parameters: { layout: 'fullscreen', viewMode: 'story', - testOptions: { skip: true }, // FIXME }, } export default meta diff --git a/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx b/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx index 747a093413c06..e99592c5176ac 100644 --- a/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx +++ b/frontend/src/scenes/insights/EmptyStates/EmptyStates.tsx @@ -1,28 +1,31 @@ -import { useActions, useValues } from 'kea' +import './EmptyStates.scss' + // eslint-disable-next-line no-restricted-imports import { PlusCircleOutlined, ThunderboltFilled } from '@ant-design/icons' +import { IconWarning } from '@posthog/icons' +import { LemonButton } from '@posthog/lemon-ui' +import { Empty } from 'antd' +import { useActions, useValues } from 'kea' +import { BuilderHog3 } from 'lib/components/hedgehogs' +import { supportLogic } from 'lib/components/Support/supportLogic' +import { SupportModal } from 'lib/components/Support/SupportModal' import { IconErrorOutline, IconInfo, IconOpenInNew, IconPlus } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { posthog } from 'posthog-js' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' import { entityFilterLogic } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' -import { Button, Empty } from 'antd' -import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { FilterType, InsightLogicProps, SavedInsightsTabs } from '~/types' import { insightLogic } from 'scenes/insights/insightLogic' -import './EmptyStates.scss' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' import { urls } from 'scenes/urls' -import { Link } from 'lib/lemon-ui/Link' -import { LemonButton } from '@posthog/lemon-ui' -import { samplingFilterLogic } from '../EditorFilters/samplingFilterLogic' -import { posthog } from 'posthog-js' -import { seriesToActionsAndEvents } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' + import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { seriesToActionsAndEvents } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { FunnelsQuery } from '~/queries/schema' -import { supportLogic } from 'lib/components/Support/supportLogic' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { BuilderHog3 } from 'lib/components/hedgehogs' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { SupportModal } from 'lib/components/Support/SupportModal' -import { IconWarning } from '@posthog/icons' +import { FilterType, InsightLogicProps, SavedInsightsTabs } from '~/types' + +import { samplingFilterLogic } from '../EditorFilters/samplingFilterLogic' export function InsightEmptyState({ heading = 'There are no matching events for this query', @@ -312,17 +315,19 @@ export function SavedInsightsEmptyState(): JSX.Element {

    {description}

    )} {tab !== SavedInsightsTabs.Favorites && ( - - - +
    + + } + className="add-insight-button" + > + New Insight + + +
    )}
    diff --git a/frontend/src/scenes/insights/Insight.tsx b/frontend/src/scenes/insights/Insight.tsx index e7a88e959f8bd..bb3bbc8280f21 100644 --- a/frontend/src/scenes/insights/Insight.tsx +++ b/frontend/src/scenes/insights/Insight.tsx @@ -1,17 +1,20 @@ import './Insight.scss' -import { useEffect } from 'react' + import { BindLogic, useActions, useMountedLogic, useValues } from 'kea' +import { useEffect } from 'react' +import { InsightPageHeader } from 'scenes/insights/InsightPageHeader' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { insightLogic } from './insightLogic' -import { insightCommandLogic } from './insightCommandLogic' -import { insightDataLogic } from './insightDataLogic' -import { InsightShortId, ItemMode } from '~/types' -import { InsightsNav } from './InsightNav/InsightsNav' import { InsightSkeleton } from 'scenes/insights/InsightSkeleton' + import { Query } from '~/queries/Query/Query' -import { InsightPageHeader } from 'scenes/insights/InsightPageHeader' -import { containsHogQLQuery, isInsightVizNode } from '~/queries/utils' import { Node } from '~/queries/schema' +import { containsHogQLQuery, isInsightVizNode } from '~/queries/utils' +import { InsightShortId, ItemMode } from '~/types' + +import { insightCommandLogic } from './insightCommandLogic' +import { insightDataLogic } from './insightDataLogic' +import { insightLogic } from './insightLogic' +import { InsightsNav } from './InsightNav/InsightsNav' export interface InsightSceneProps { insightId: InsightShortId | 'new' } diff --git a/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx b/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx index ee6541901a058..2f21d90494199 100644 --- a/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx +++ b/frontend/src/scenes/insights/InsightNav/InsightsNav.tsx @@ -1,12 +1,13 @@ import { useActions, useValues } from 'kea' -import { insightLogic } from '../insightLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { FunnelsCue } from '../views/Trends/FunnelsCue' -import { INSIGHT_TYPES_METADATA } from 'scenes/saved-insights/SavedInsights' -import { Link } from 'lib/lemon-ui/Link' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Link } from 'lib/lemon-ui/Link' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { insightNavLogic } from 'scenes/insights/InsightNav/insightNavLogic' import { insightTypeURL } from 'scenes/insights/utils' +import { INSIGHT_TYPES_METADATA } from 'scenes/saved-insights/SavedInsights' + +import { insightLogic } from '../insightLogic' +import { FunnelsCue } from '../views/Trends/FunnelsCue' export function InsightsNav(): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/InsightNav/insightNavLogic.test.ts b/frontend/src/scenes/insights/InsightNav/insightNavLogic.test.ts index ca4602559150a..ba1bd58000029 100644 --- a/frontend/src/scenes/insights/InsightNav/insightNavLogic.test.ts +++ b/frontend/src/scenes/insights/InsightNav/insightNavLogic.test.ts @@ -1,13 +1,15 @@ -import { insightLogic } from 'scenes/insights/insightLogic' -import { FunnelVizType, InsightLogicProps, InsightShortId, InsightType, StepOrderValue } from '~/types' -import { insightNavLogic } from 'scenes/insights/InsightNav/insightNavLogic' import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightNavLogic } from 'scenes/insights/InsightNav/insightNavLogic' + import { useMocks } from '~/mocks/jest' +import { nodeKindToDefaultQuery } from '~/queries/nodes/InsightQuery/defaults' import { InsightVizNode, Node, NodeKind } from '~/queries/schema' +import { initKeaTests } from '~/test/init' +import { FunnelVizType, InsightLogicProps, InsightShortId, InsightType, StepOrderValue } from '~/types' + import { insightDataLogic } from '../insightDataLogic' -import { nodeKindToDefaultQuery } from '~/queries/nodes/InsightQuery/defaults' describe('insightNavLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx b/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx index f77c23537e581..3d9936869b22e 100644 --- a/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx +++ b/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx @@ -1,44 +1,45 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' - -import { InsightLogicProps, InsightType, ActionFilter } from '~/types' -import type { insightNavLogicType } from './insightNavLogicType' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' import { insightLogic } from 'scenes/insights/insightLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' + +import { examples, TotalEventsTable } from '~/queries/examples' +import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { insightMap } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { getDisplay, getShowPercentStackView, getShowValueOnSeries } from '~/queries/nodes/InsightViz/utils' import { - InsightVizNode, + ActionsNode, + EventsNode, + FunnelsFilter, + FunnelsQuery, InsightQueryNode, + InsightVizNode, + LifecycleFilter, + LifecycleQuery, NodeKind, - TrendsQuery, - FunnelsQuery, - RetentionQuery, + PathsFilter, PathsQuery, - StickinessQuery, - LifecycleQuery, - TrendsFilter, - FunnelsFilter, RetentionFilter, - PathsFilter, + RetentionQuery, StickinessFilter, - LifecycleFilter, - EventsNode, - ActionsNode, + StickinessQuery, + TrendsFilter, + TrendsQuery, } from '~/queries/schema' -import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { insightMap } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { - isInsightVizNode, - isRetentionQuery, + containsHogQLQuery, + filterKeyForQuery, isInsightQueryWithBreakdown, isInsightQueryWithSeries, - filterKeyForQuery, - containsHogQLQuery, + isInsightVizNode, + isRetentionQuery, } from '~/queries/utils' -import { examples, TotalEventsTable } from '~/queries/examples' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { actionsAndEventsToSeries } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { getDisplay, getShowPercentStackView, getShowValueOnSeries } from '~/queries/nodes/InsightViz/utils' -import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' +import { ActionFilter, InsightLogicProps, InsightType } from '~/types' + +import type { insightNavLogicType } from './insightNavLogicType' export interface Tab { label: string | JSX.Element diff --git a/frontend/src/scenes/insights/InsightPageHeader.tsx b/frontend/src/scenes/insights/InsightPageHeader.tsx index be1c74bb77cc6..c100746612e11 100644 --- a/frontend/src/scenes/insights/InsightPageHeader.tsx +++ b/frontend/src/scenes/insights/InsightPageHeader.tsx @@ -1,48 +1,48 @@ +import { useActions, useMountedLogic, useValues } from 'kea' +import { router } from 'kea-router' +import { AddToDashboard } from 'lib/components/AddToDashboard/AddToDashboard' +import { AddToDashboardModal } from 'lib/components/AddToDashboard/AddToDashboardModal' import { EditableField } from 'lib/components/EditableField/EditableField' - -import { - AvailableFeature, - ExporterFormat, - InsightLogicProps, - InsightModel, - InsightShortId, - ItemMode, - NotebookNodeType, -} from '~/types' +import { ExportButton } from 'lib/components/ExportButton/ExportButton' +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 { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' import { IconLock } from 'lib/lemon-ui/icons' -import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { urls } from 'scenes/urls' -import { SubscribeButton, SubscriptionsModal } from 'lib/components/Subscriptions/SubscriptionsModal' -import { ExportButton } from 'lib/components/ExportButton/ExportButton' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { AddToDashboard } from 'lib/components/AddToDashboard/AddToDashboard' +import { useState } from 'react' +import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal' +import { insightCommandLogic } from 'scenes/insights/insightCommandLogic' +import { insightDataLogic } from 'scenes/insights/insightDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' import { InsightSaveButton } from 'scenes/insights/InsightSaveButton' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' -import { PageHeader } from 'lib/components/PageHeader' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { insightLogic } from 'scenes/insights/insightLogic' +import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { insightCommandLogic } from 'scenes/insights/insightCommandLogic' +import { teamLogic } from 'scenes/teamLogic' +import { mathsLogic } from 'scenes/trends/mathsLogic' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { groupsModel } from '~/models/groupsModel' + import { cohortsModel } from '~/models/cohortsModel' -import { mathsLogic } from 'scenes/trends/mathsLogic' +import { groupsModel } from '~/models/groupsModel' import { tagsModel } from '~/models/tagsModel' -import { teamLogic } from 'scenes/teamLogic' -import { useActions, useMountedLogic, useValues } from 'kea' -import { router } from 'kea-router' -import { SharingModal } from 'lib/components/Sharing/SharingModal' -import { isInsightVizNode } from '~/queries/utils' -import { summarizeInsight } from 'scenes/insights/summarizeInsight' -import { AddToDashboardModal } from 'lib/components/AddToDashboard/AddToDashboardModal' -import { useState } from 'react' -import { NewDashboardModal } from 'scenes/dashboard/NewDashboardModal' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { DataTableNode, NodeKind } from '~/queries/schema' +import { isInsightVizNode } from '~/queries/utils' +import { + AvailableFeature, + ExporterFormat, + InsightLogicProps, + InsightModel, + InsightShortId, + ItemMode, + NotebookNodeType, +} from '~/types' export function InsightPageHeader({ insightLogicProps }: { insightLogicProps: InsightLogicProps }): JSX.Element { // insightSceneLogic diff --git a/frontend/src/scenes/insights/InsightScene.tsx b/frontend/src/scenes/insights/InsightScene.tsx index 5a8dda166f6ca..99e1e3a5ae23a 100644 --- a/frontend/src/scenes/insights/InsightScene.tsx +++ b/frontend/src/scenes/insights/InsightScene.tsx @@ -1,9 +1,9 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' import { useValues } from 'kea' +import { NotFound } from 'lib/components/NotFound' import { Insight } from 'scenes/insights/Insight' +import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' import { InsightSkeleton } from 'scenes/insights/InsightSkeleton' -import { NotFound } from 'lib/components/NotFound' +import { SceneExport } from 'scenes/sceneTypes' export function InsightScene(): JSX.Element { const { insightId, insight, insightLogicRef } = useValues(insightSceneLogic) diff --git a/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.stories.tsx b/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.stories.tsx index 0ed7370ac4ec5..af69bc1bf967e 100644 --- a/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.stories.tsx +++ b/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.stories.tsx @@ -1,10 +1,12 @@ -import { InsightTooltip } from './InsightTooltip' -import { cohortsModel } from '~/models/cohortsModel' -import { useMountedLogic } from 'kea' import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { InsightTooltipProps } from './insightTooltipUtils' -import { humanFriendlyNumber } from 'lib/utils' +import { useMountedLogic } from 'kea' import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { humanFriendlyNumber } from 'lib/utils' + +import { cohortsModel } from '~/models/cohortsModel' + +import { InsightTooltip } from './InsightTooltip' +import { InsightTooltipProps } from './insightTooltipUtils' const data = { date: '2022-08-31', @@ -130,9 +132,7 @@ const meta: Meta = { renderSeries: (value) => value, groupTypeLabel: 'people', }, - parameters: { - testOptions: { skip: true }, // FIXME: The InWrapper story fails at locator.screenshot() for some reason - }, + tags: ['test-skip'], // FIXME: The InWrapper story fails at locator.screenshot() for some reason } export default meta diff --git a/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.tsx b/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.tsx index 8025d203d7d5e..9d4ad4b8b7664 100644 --- a/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.tsx +++ b/frontend/src/scenes/insights/InsightTooltip/InsightTooltip.tsx @@ -1,22 +1,25 @@ import './InsightTooltip.scss' -import { ReactNode } from 'react' + +import { useValues } from 'kea' +import { InsightLabel } from 'lib/components/InsightLabel' +import { IconHandClick } from 'lib/lemon-ui/icons' import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { shortTimeZone } from 'lib/utils' +import { ReactNode } from 'react' +import { formatAggregationValue } from 'scenes/insights/utils' + +import { FormatPropertyValueForDisplayFunction, propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' + import { COL_CUTOFF, - ROW_CUTOFF, + getFormattedDate, getTooltipTitle, InsightTooltipProps, invertDataSource, InvertedSeriesDatum, + ROW_CUTOFF, SeriesDatum, - getFormattedDate, } from './insightTooltipUtils' -import { InsightLabel } from 'lib/components/InsightLabel' -import { IconHandClick } from 'lib/lemon-ui/icons' -import { shortTimeZone } from 'lib/utils' -import { useValues } from 'kea' -import { FormatPropertyValueForDisplayFunction, propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatAggregationValue } from 'scenes/insights/utils' export function ClickToInspectActors({ isTruncated, diff --git a/frontend/src/scenes/insights/InsightTooltip/LEGACY_InsightTooltip.tsx b/frontend/src/scenes/insights/InsightTooltip/LEGACY_InsightTooltip.tsx index 9d39b3c637dbd..80dfb1d18c0df 100644 --- a/frontend/src/scenes/insights/InsightTooltip/LEGACY_InsightTooltip.tsx +++ b/frontend/src/scenes/insights/InsightTooltip/LEGACY_InsightTooltip.tsx @@ -1,7 +1,9 @@ +import './LEGACY_InsightTooltip.scss' + import { DateDisplay } from 'lib/components/DateDisplay' import { IconHandClick } from 'lib/lemon-ui/icons' + import { IntervalType } from '~/types' -import './LEGACY_InsightTooltip.scss' interface BodyLine { id?: string | number diff --git a/frontend/src/scenes/insights/InsightTooltip/insightTooltipUtils.tsx b/frontend/src/scenes/insights/InsightTooltip/insightTooltipUtils.tsx index 6794d6a112827..21018d4162656 100644 --- a/frontend/src/scenes/insights/InsightTooltip/insightTooltipUtils.tsx +++ b/frontend/src/scenes/insights/InsightTooltip/insightTooltipUtils.tsx @@ -1,10 +1,12 @@ import { dayjs } from 'lib/dayjs' -import { ActionFilter, CompareLabelType, FilterType, IntervalType } from '~/types' import { capitalizeFirstLetter, midEllipsis, pluralize } from 'lib/utils' +import { isTrendsFilter } from 'scenes/insights/sharedUtils' + import { cohortsModel } from '~/models/cohortsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { ActionFilter, CompareLabelType, FilterType, IntervalType } from '~/types' + import { formatBreakdownLabel } from '../utils' -import { isTrendsFilter } from 'scenes/insights/sharedUtils' export interface SeriesDatum { id: number // determines order that series will be displayed in diff --git a/frontend/src/scenes/insights/Insights.stories.tsx b/frontend/src/scenes/insights/Insights.stories.tsx index 1c8a87112cf04..ea0532b199365 100644 --- a/frontend/src/scenes/insights/Insights.stories.tsx +++ b/frontend/src/scenes/insights/Insights.stories.tsx @@ -1,8 +1,9 @@ import { Meta, StoryObj } from '@storybook/react' -import { mswDecorator } from '~/mocks/browser' -import { samplePersonProperties, sampleRetentionPeopleResponse } from 'scenes/insights/__mocks__/insight.mocks' -import { createInsightStory } from 'scenes/insights/__mocks__/createInsightScene' import { App } from 'scenes/App' +import { createInsightStory } from 'scenes/insights/__mocks__/createInsightScene' +import { samplePersonProperties, sampleRetentionPeopleResponse } from 'scenes/insights/__mocks__/insight.mocks' + +import { mswDecorator } from '~/mocks/browser' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/scenes/insights/RetentionDatePicker.tsx b/frontend/src/scenes/insights/RetentionDatePicker.tsx index 4af9318d3078d..19ff93bfa9060 100644 --- a/frontend/src/scenes/insights/RetentionDatePicker.tsx +++ b/frontend/src/scenes/insights/RetentionDatePicker.tsx @@ -1,8 +1,8 @@ import { useActions, useValues } from 'kea' +import { DatePicker } from 'lib/components/DatePicker' +import { dayjs } from 'lib/dayjs' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { insightLogic } from 'scenes/insights/insightLogic' -import { dayjs } from 'lib/dayjs' -import { DatePicker } from 'lib/components/DatePicker' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' export function RetentionDatePicker(): JSX.Element { diff --git a/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx b/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx index 016ae3005bc2b..95cca49b7cc7f 100644 --- a/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx +++ b/frontend/src/scenes/insights/__mocks__/createInsightScene.tsx @@ -1,10 +1,11 @@ -import { InsightModel } from '~/types' -import { setFeatureFlags, useStorybookMocks } from '~/mocks/browser' -import { useEffect } from 'react' +import { StoryFn } from '@storybook/react' import { router } from 'kea-router' -import { App } from 'scenes/App' import { FEATURE_FLAGS } from 'lib/constants' -import { StoryFn } from '@storybook/react' +import { useEffect } from 'react' +import { App } from 'scenes/App' + +import { setFeatureFlags, useStorybookMocks } from '~/mocks/browser' +import { InsightModel } from '~/types' let shortCounter = 0 export function createInsightStory( diff --git a/frontend/src/scenes/insights/aggregationAxisFormat.ts b/frontend/src/scenes/insights/aggregationAxisFormat.ts index 0ffa78d829b4f..55aa3b48a2bb2 100644 --- a/frontend/src/scenes/insights/aggregationAxisFormat.ts +++ b/frontend/src/scenes/insights/aggregationAxisFormat.ts @@ -1,5 +1,6 @@ import { LemonSelectOptionLeaf } from 'lib/lemon-ui/LemonSelect' import { humanFriendlyDuration, humanFriendlyNumber, percentage } from 'lib/utils' + import { TrendsFilter } from '~/queries/schema' import { ChartDisplayType, TrendsFilterType } from '~/types' diff --git a/frontend/src/scenes/insights/aggregationAxisFormats.test.ts b/frontend/src/scenes/insights/aggregationAxisFormats.test.ts index 95582e5b81aa6..56db792c0a3e3 100644 --- a/frontend/src/scenes/insights/aggregationAxisFormats.test.ts +++ b/frontend/src/scenes/insights/aggregationAxisFormats.test.ts @@ -1,4 +1,5 @@ import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' + import { FilterType } from '~/types' describe('formatAggregationAxisValue', () => { diff --git a/frontend/src/scenes/insights/common.tsx b/frontend/src/scenes/insights/common.tsx index ac5bf6b2319b7..4c50d2bfeb146 100644 --- a/frontend/src/scenes/insights/common.tsx +++ b/frontend/src/scenes/insights/common.tsx @@ -1,7 +1,8 @@ -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import clsx from 'clsx' import '../../lib/components/PropertyGroupFilters/PropertyGroupFilters.scss' + +import clsx from 'clsx' import { IconInfo } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' export function GlobalFiltersTitle({ unit = 'series', diff --git a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.stories.tsx b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.stories.tsx index d626ed305f84d..227749eee54cd 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.stories.tsx +++ b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.stories.tsx @@ -1,16 +1,18 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' +import { useMountedLogic, useValues } from 'kea' +import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { SINGLE_SERIES_DISPLAY_TYPES } from 'lib/constants' +import { alphabet, uuid } from 'lib/utils' import { useRef, useState } from 'react' -import { ActionFilter, ActionFilterProps } from './ActionFilter' +import { isFilterWithDisplay, isLifecycleFilter } from 'scenes/insights/sharedUtils' + import { cohortsModel } from '~/models/cohortsModel' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { taxonomicFilterMocksDecorator } from 'lib/components/TaxonomicFilter/__mocks__/taxonomicFilterMocksDecorator' -import { useMountedLogic, useValues } from 'kea' +import { groupsModel } from '~/models/groupsModel' import { FilterType, InsightType } from '~/types' + +import { ActionFilter, ActionFilterProps } from './ActionFilter' import { MathAvailability } from './ActionFilterRow/ActionFilterRow' -import { groupsModel } from '~/models/groupsModel' -import { alphabet, uuid } from 'lib/utils' -import { Meta, StoryFn, StoryObj } from '@storybook/react' -import { SINGLE_SERIES_DISPLAY_TYPES } from 'lib/constants' -import { isFilterWithDisplay, isLifecycleFilter } from 'scenes/insights/sharedUtils' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.tsx b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.tsx index adcbb55787bb9..9768b5a9114da 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.tsx +++ b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilter.tsx @@ -1,20 +1,23 @@ import './ActionFilter.scss' -import React, { useEffect } from 'react' + +import { DndContext } from '@dnd-kit/core' +import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' +import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable' +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' -import { entityFilterLogic, toFilters, LocalFilter } from './entityFilterLogic' -import { ActionFilterRow, MathAvailability } from './ActionFilterRow/ActionFilterRow' -import { ActionFilter as ActionFilterType, FilterType, FunnelExclusion, InsightType, Optional } from '~/types' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { RenameModal } from 'scenes/insights/filters/ActionFilter/RenameModal' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { teamLogic } from '../../../teamLogic' -import clsx from 'clsx' -import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' import { IconPlusMini } from 'lib/lemon-ui/icons' -import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable' -import { DndContext } from '@dnd-kit/core' -import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' +import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' import { verticalSortableListCollisionDetection } from 'lib/sortable' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import React, { useEffect } from 'react' +import { RenameModal } from 'scenes/insights/filters/ActionFilter/RenameModal' + +import { ActionFilter as ActionFilterType, FilterType, FunnelExclusion, InsightType, Optional } from '~/types' + +import { teamLogic } from '../../../teamLogic' +import { ActionFilterRow, MathAvailability } from './ActionFilterRow/ActionFilterRow' +import { entityFilterLogic, LocalFilter, toFilters } from './entityFilterLogic' export interface ActionFilterProps { setFilters: (filters: FilterType) => void diff --git a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx index 26b736f8e28f3..528b9fab025d2 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx +++ b/frontend/src/scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow.tsx @@ -1,23 +1,26 @@ +import './ActionFilterRow.scss' + +import { DraggableSyntheticListeners } from '@dnd-kit/core' +import { useSortable } from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' +import { LemonSelect, LemonSelectOption, LemonSelectOptions } from '@posthog/lemon-ui' import { BuiltLogic, useActions, useValues } from 'kea' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { - ActionFilter as ActionFilterType, - ActionFilter, - EntityType, - EntityTypes, - FunnelExclusion, - PropertyFilterValue, - BaseMathType, - PropertyMathType, - CountPerActorMathType, - HogQLMathType, -} from '~/types' +import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' +import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { getEventNamesForAction } from 'lib/utils' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { SeriesGlyph, SeriesLetter } from 'lib/components/SeriesGlyph' -import './ActionFilterRow.scss' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' +import { TaxonomicPopover, TaxonomicStringPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' +import { IconCopy, IconDelete, IconEdit, IconFilter, IconWithCount } from 'lib/lemon-ui/icons' +import { SortableDragIcon } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonDropdown } from 'lib/lemon-ui/LemonDropdown' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { getEventNamesForAction } from 'lib/utils' +import { useState } from 'react' +import { GroupIntroductionFooter } from 'scenes/groups/GroupsIntroduction' +import { isAllEventsEntityFilter } from 'scenes/insights/utils' import { apiValueToMathType, COUNT_PER_ACTOR_MATH_DEFINITIONS, @@ -26,23 +29,23 @@ import { mathTypeToApiValues, PROPERTY_MATH_DEFINITIONS, } from 'scenes/trends/mathsLogic' + import { actionsModel } from '~/models/actionsModel' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { TaxonomicPopover, TaxonomicStringPopover } from 'lib/components/TaxonomicPopover/TaxonomicPopover' -import { IconCopy, IconDelete, IconEdit, IconFilter, IconWithCount } from 'lib/lemon-ui/icons' -import { SortableDragIcon } from 'lib/lemon-ui/icons' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonSelect, LemonSelectOption, LemonSelectOptions } from '@posthog/lemon-ui' -import { useState } from 'react' -import { GroupIntroductionFooter } from 'scenes/groups/GroupsIntroduction' -import { LemonDropdown } from 'lib/lemon-ui/LemonDropdown' -import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor' -import { entityFilterLogicType } from '../entityFilterLogicType' -import { isAllEventsEntityFilter } from 'scenes/insights/utils' -import { useSortable } from '@dnd-kit/sortable' -import { CSS } from '@dnd-kit/utilities' +import { + ActionFilter, + ActionFilter as ActionFilterType, + BaseMathType, + CountPerActorMathType, + EntityType, + EntityTypes, + FunnelExclusion, + HogQLMathType, + PropertyFilterValue, + PropertyMathType, +} from '~/types' + import { LocalFilter } from '../entityFilterLogic' -import { DraggableSyntheticListeners } from '@dnd-kit/core' +import { entityFilterLogicType } from '../entityFilterLogicType' const DragHandle = (props: DraggableSyntheticListeners | undefined): JSX.Element => ( diff --git a/frontend/src/scenes/insights/filters/ActionFilter/RenameModal.tsx b/frontend/src/scenes/insights/filters/ActionFilter/RenameModal.tsx index 8e98a5fb83159..30609a83af527 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/RenameModal.tsx +++ b/frontend/src/scenes/insights/filters/ActionFilter/RenameModal.tsx @@ -1,9 +1,10 @@ +import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { entityFilterLogic } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' -import { InsightType } from '~/types' -import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' import { renameModalLogic } from 'scenes/insights/filters/ActionFilter/renameModalLogic' -import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' +import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' + +import { InsightType } from '~/types' interface RenameModalProps { typeKey: string diff --git a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.test.ts b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.test.ts index dfeb640ded0a8..d4f6cf1840f2d 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.test.ts +++ b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.test.ts @@ -1,11 +1,13 @@ -import { entityFilterLogic, toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' import { expectLogic } from 'kea-test-utils' +import * as libUtils from 'lib/utils' +import { entityFilterLogic, toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' + +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' -import filtersJson from './__mocks__/filters.json' -import eventDefinitionsJson from './__mocks__/event_definitions.json' import { FilterType } from '~/types' -import { useMocks } from '~/mocks/jest' -import * as libUtils from 'lib/utils' + +import eventDefinitionsJson from './__mocks__/event_definitions.json' +import filtersJson from './__mocks__/filters.json' describe('entityFilterLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts index 0529a2d311838..0d5e88b045137 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts +++ b/frontend/src/scenes/insights/filters/ActionFilter/entityFilterLogic.ts @@ -1,9 +1,11 @@ -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import { EntityTypes, FilterType, Entity, EntityType, ActionFilter, EntityFilter, AnyPropertyFilter } from '~/types' -import type { entityFilterLogicType } from './entityFilterLogicType' -import { eventUsageLogic, GraphSeriesAddedSource } from 'lib/utils/eventUsageLogic' -import { uuid } from 'lib/utils' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' +import { uuid } from 'lib/utils' +import { eventUsageLogic, GraphSeriesAddedSource } from 'lib/utils/eventUsageLogic' + +import { ActionFilter, AnyPropertyFilter, Entity, EntityFilter, EntityType, EntityTypes, FilterType } from '~/types' + +import type { entityFilterLogicType } from './entityFilterLogicType' export type LocalFilter = ActionFilter & { order: number diff --git a/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.test.ts b/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.test.ts index 3b62100791455..e6bcec0dafe4c 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.test.ts +++ b/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.test.ts @@ -1,10 +1,12 @@ -import { entityFilterLogic } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' import { expectLogic } from 'kea-test-utils' -import filtersJson from './__mocks__/filters.json' -import { EntityFilter } from '~/types' +import { entityFilterLogic } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' import { renameModalLogic } from 'scenes/insights/filters/ActionFilter/renameModalLogic' import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' + import { initKeaTests } from '~/test/init' +import { EntityFilter } from '~/types' + +import filtersJson from './__mocks__/filters.json' describe('renameModalLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.ts b/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.ts index 82e1a81421993..54e868661cf86 100644 --- a/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.ts +++ b/frontend/src/scenes/insights/filters/ActionFilter/renameModalLogic.ts @@ -1,8 +1,10 @@ -import { kea, props, key, path, connect, actions, reducers } from 'kea' -import type { renameModalLogicType } from './renameModalLogicType' -import { EntityFilterTypes } from '~/types' -import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' +import { actions, connect, kea, key, path, props, reducers } from 'kea' import { entityFilterLogic } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' +import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' + +import { EntityFilterTypes } from '~/types' + +import type { renameModalLogicType } from './renameModalLogicType' export interface RenameModalProps { filter: EntityFilterTypes diff --git a/frontend/src/scenes/insights/filters/AggregationSelect.tsx b/frontend/src/scenes/insights/filters/AggregationSelect.tsx index 97104d7f22315..67b8872c065c9 100644 --- a/frontend/src/scenes/insights/filters/AggregationSelect.tsx +++ b/frontend/src/scenes/insights/filters/AggregationSelect.tsx @@ -1,13 +1,14 @@ -import { useActions, useValues } from 'kea' -import { groupsModel } from '~/models/groupsModel' import { LemonSelect, LemonSelectSection } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor' import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' import { GroupIntroductionFooter } from 'scenes/groups/GroupsIntroduction' -import { InsightLogicProps } from '~/types' -import { isFunnelsQuery, isInsightQueryNode, isLifecycleQuery, isStickinessQuery } from '~/queries/utils' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' + +import { groupsModel } from '~/models/groupsModel' import { FunnelsQuery } from '~/queries/schema' -import { HogQLEditor } from 'lib/components/HogQLEditor/HogQLEditor' +import { isFunnelsQuery, isInsightQueryNode, isLifecycleQuery, isStickinessQuery } from '~/queries/utils' +import { InsightLogicProps } from '~/types' function getHogQLValue(groupIndex?: number, aggregationQuery?: string): string { if (groupIndex !== undefined) { diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTag.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTag.tsx index 8437268b15c9a..045b06d4865b5 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTag.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTag.tsx @@ -1,19 +1,20 @@ -import { useState } from 'react' -import { BindLogic, useActions, useValues } from 'kea' +import './BreakdownTag.scss' import { LemonTag, LemonTagProps } from '@posthog/lemon-ui' +import { BindLogic, useActions, useValues } from 'kea' +import { HoqQLPropertyInfo } from 'lib/components/HoqQLPropertyInfo' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { breakdownTagLogic } from './breakdownTagLogic' -import { BreakdownTagMenu } from './BreakdownTagMenu' -import { BreakdownType } from '~/types' -import { TaxonomicBreakdownPopover } from './TaxonomicBreakdownPopover' import { PopoverReferenceContext } from 'lib/lemon-ui/Popover/Popover' -import { HoqQLPropertyInfo } from 'lib/components/HoqQLPropertyInfo' +import { useState } from 'react' +import { insightLogic } from 'scenes/insights/insightLogic' + import { cohortsModel } from '~/models/cohortsModel' -import { isAllCohort, isCohort } from './taxonomicBreakdownFilterUtils' +import { BreakdownType } from '~/types' -import './BreakdownTag.scss' -import { insightLogic } from 'scenes/insights/insightLogic' +import { breakdownTagLogic } from './breakdownTagLogic' +import { BreakdownTagMenu } from './BreakdownTagMenu' +import { isAllCohort, isCohort } from './taxonomicBreakdownFilterUtils' +import { TaxonomicBreakdownPopover } from './TaxonomicBreakdownPopover' type EditableBreakdownTagProps = { breakdown: string | number diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTagMenu.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTagMenu.tsx index 49cbafae798c7..ae89f81f09966 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTagMenu.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/BreakdownTagMenu.tsx @@ -1,14 +1,13 @@ -import { useActions, useValues } from 'kea' +import './BreakdownTagMenu.scss' import { LemonButton, LemonDivider, LemonInput, LemonSwitch } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { useActions, useValues } from 'kea' import { IconInfo } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { breakdownTagLogic } from './breakdownTagLogic' import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' -import './BreakdownTagMenu.scss' - export const BreakdownTagMenu = (): JSX.Element => { const { isHistogramable, isNormalizeable } = useValues(breakdownTagLogic) const { removeBreakdown } = useActions(breakdownTagLogic) diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx index d61cb0bf2b47d..b4d3f7b920026 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx @@ -1,8 +1,9 @@ -import { useState } from 'react' -import { useValues } from 'kea' import { LemonButton } from '@posthog/lemon-ui' +import { useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { IconPlusMini } from 'lib/lemon-ui/icons' +import { useState } from 'react' + import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' import { TaxonomicBreakdownPopover } from './TaxonomicBreakdownPopover' diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx index 0bd1856e2205a..6b98d27118390 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx @@ -1,7 +1,9 @@ import { BindLogic, useValues } from 'kea' import { TaxonomicBreakdownButton } from 'scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton' + import { BreakdownFilter } from '~/queries/schema' import { ChartDisplayType, InsightLogicProps } from '~/types' + import { EditableBreakdownTag } from './BreakdownTag' import { taxonomicBreakdownFilterLogic, TaxonomicBreakdownFilterLogicProps } from './taxonomicBreakdownFilterLogic' diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx index fe3f64a0baa54..24be984cf9b84 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx @@ -1,12 +1,12 @@ import { useActions, useValues } from 'kea' +import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' +import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { insightLogic } from 'scenes/insights/insightLogic' import { groupsModel } from '~/models/groupsModel' -import { insightLogic } from 'scenes/insights/insightLogic' -import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { TaxonomicFilter } from 'lib/components/TaxonomicFilter/TaxonomicFilter' -import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types' +import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' type TaxonomicBreakdownPopoverProps = { open: boolean diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/breakdownTagLogic.ts b/frontend/src/scenes/insights/filters/BreakdownFilter/breakdownTagLogic.ts index 14f98e93a4883..f7dc476e70840 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/breakdownTagLogic.ts +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/breakdownTagLogic.ts @@ -1,13 +1,14 @@ import { actions, connect, kea, key, listeners, path, props, selectors } from 'kea' +import { propertyFilterTypeToPropertyDefinitionType } from 'lib/components/PropertyFilters/utils' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { InsightLogicProps } from '~/types' import type { breakdownTagLogicType } from './breakdownTagLogicType' import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' import { isURLNormalizeable } from './taxonomicBreakdownFilterUtils' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { cohortsModel } from '~/models/cohortsModel' -import { propertyFilterTypeToPropertyDefinitionType } from 'lib/components/PropertyFilters/utils' -import { InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' export interface BreakdownTagLogicProps { insightProps: InsightLogicProps diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.test.ts b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.test.ts index e2ae3a3e84ba1..93540de467b96 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.test.ts +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.test.ts @@ -1,11 +1,11 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' - import { TaxonomicFilterGroup, TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' +import { initKeaTests } from '~/test/init' import { InsightLogicProps } from '~/types' +import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' + const taxonomicGroupFor = ( type: TaxonomicFilterGroupType, groupTypeIndex: number | undefined = undefined diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts index b6583ebda2cbd..ef802a4a6495e 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts @@ -9,13 +9,14 @@ import { TaxonomicFilterGroupType, TaxonomicFilterValue, } from 'lib/components/TaxonomicFilter/types' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' import { BreakdownFilter } from '~/queries/schema' -import { isCohortBreakdown, isURLNormalizeable } from './taxonomicBreakdownFilterUtils' import { BreakdownType, ChartDisplayType, InsightLogicProps } from '~/types' import type { taxonomicBreakdownFilterLogicType } from './taxonomicBreakdownFilterLogicType' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { isCohortBreakdown, isURLNormalizeable } from './taxonomicBreakdownFilterUtils' export type TaxonomicBreakdownFilterLogicProps = { insightProps: InsightLogicProps diff --git a/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/ExclusionRowSuffix.tsx b/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/ExclusionRowSuffix.tsx index f77d781c56ee5..50609fa5cfeef 100644 --- a/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/ExclusionRowSuffix.tsx +++ b/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/ExclusionRowSuffix.tsx @@ -1,15 +1,16 @@ +import { LemonButton } from '@posthog/lemon-ui' import { Select } from 'antd' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { ANTD_TOOLTIP_PLACEMENTS } from 'lib/utils' -import { FunnelExclusion, ActionFilter as ActionFilterType, FunnelsFilterType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonButton } from '@posthog/lemon-ui' import { IconDelete } from 'lib/lemon-ui/icons' -import { FunnelsQuery } from '~/queries/schema' +import { ANTD_TOOLTIP_PLACEMENTS } from 'lib/utils' import { getClampedStepRangeFilterDataExploration } from 'scenes/funnels/funnelUtils' -import clsx from 'clsx' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { FunnelsQuery } from '~/queries/schema' +import { ActionFilter as ActionFilterType, FunnelExclusion, FunnelsFilterType } from '~/types' + type ExclusionRowSuffixComponentBaseProps = { filter: ActionFilterType | FunnelExclusion index: number diff --git a/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/FunnelExclusionsFilter.tsx b/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/FunnelExclusionsFilter.tsx index bcaa3a2c846fa..a9ce4de510ff4 100644 --- a/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/FunnelExclusionsFilter.tsx +++ b/frontend/src/scenes/insights/filters/FunnelExclusionsFilter/FunnelExclusionsFilter.tsx @@ -1,15 +1,17 @@ -import { useRef } from 'react' -import { useActions, useValues } from 'kea' import useSize from '@react-hook/size' -import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' +import { useActions, useValues } from 'kea' import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { FunnelExclusion, EntityTypes, FilterType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' +import { useRef } from 'react' +import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' -import { ExclusionRowSuffix } from './ExclusionRowSuffix' -import { ExclusionRow } from './ExclusionRow' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { EntityTypes, FilterType, FunnelExclusion } from '~/types' + +import { ExclusionRow } from './ExclusionRow' +import { ExclusionRowSuffix } from './ExclusionRowSuffix' export function FunnelExclusionsFilter(): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/filters/FunnelStepReferencePicker.tsx b/frontend/src/scenes/insights/filters/FunnelStepReferencePicker.tsx index c9cf20baf7c5c..b8b9cedf1f362 100644 --- a/frontend/src/scenes/insights/filters/FunnelStepReferencePicker.tsx +++ b/frontend/src/scenes/insights/filters/FunnelStepReferencePicker.tsx @@ -1,11 +1,10 @@ +import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' -import { FunnelStepReference } from '~/types' -import { LemonSelect } from '@posthog/lemon-ui' import { FunnelsFilter } from '~/queries/schema' +import { FunnelStepReference } from '~/types' export function FunnelStepReferencePicker(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx b/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx index 904f630ac42b6..c4b81e4a667ad 100644 --- a/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx +++ b/frontend/src/scenes/insights/filters/InsightDateFilter/InsightDateFilter.tsx @@ -1,9 +1,9 @@ -import { useValues, useActions } from 'kea' +import { IconCalendar, IconInfo } from '@posthog/icons' +import { Tooltip } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { DateFilter } from 'lib/components/DateFilter/DateFilter' import { insightLogic } from 'scenes/insights/insightLogic' -import { IconCalendar, IconInfo } from '@posthog/icons' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { Tooltip } from '@posthog/lemon-ui' type InsightDateFilterProps = { disabled: boolean diff --git a/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts b/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts index 76c8b4da0321c..c4449574549a0 100644 --- a/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts +++ b/frontend/src/scenes/insights/filters/InsightDateFilter/insightDateFilterLogic.ts @@ -1,8 +1,10 @@ -import { kea, props, key, path, connect, actions, selectors, listeners } from 'kea' -import type { insightDateFilterLogicType } from './insightDateFilterLogicType' -import { InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { actions, connect, kea, key, listeners, path, props, selectors } from 'kea' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { InsightLogicProps } from '~/types' + +import type { insightDateFilterLogicType } from './insightDateFilterLogicType' export const insightDateFilterLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/scenes/insights/filters/PathCleaningFilter.tsx b/frontend/src/scenes/insights/filters/PathCleaningFilter.tsx index 067a9ccb87850..5c482f83e0528 100644 --- a/frontend/src/scenes/insights/filters/PathCleaningFilter.tsx +++ b/frontend/src/scenes/insights/filters/PathCleaningFilter.tsx @@ -1,12 +1,11 @@ +import { LemonSwitch } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' - +import { PathCleanFilters } from 'lib/components/PathCleanFilters/PathCleanFilters' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' import { teamLogic } from 'scenes/teamLogic' import { EditorFilterProps } from '~/types' -import { LemonSwitch } from '@posthog/lemon-ui' -import { PathCleanFilters } from 'lib/components/PathCleanFilters/PathCleanFilters' -import { Tooltip } from 'lib/lemon-ui/Tooltip' export function PathCleaningFilter({ insightProps }: EditorFilterProps): JSX.Element { const { pathsFilter } = useValues(pathsDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/filters/RetentionReferencePicker.tsx b/frontend/src/scenes/insights/filters/RetentionReferencePicker.tsx index a5f9d72f2b649..f185ce8f755cd 100644 --- a/frontend/src/scenes/insights/filters/RetentionReferencePicker.tsx +++ b/frontend/src/scenes/insights/filters/RetentionReferencePicker.tsx @@ -1,8 +1,8 @@ -import { Select } from 'antd' // eslint-disable-next-line no-restricted-imports import { PercentageOutlined } from '@ant-design/icons' -import { insightLogic } from 'scenes/insights/insightLogic' +import { Select } from 'antd' import { useActions, useValues } from 'kea' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' export function RetentionReferencePicker(): JSX.Element { diff --git a/frontend/src/scenes/insights/filters/TestAccountFilter/TestAccountFilter.tsx b/frontend/src/scenes/insights/filters/TestAccountFilter/TestAccountFilter.tsx index 19eeb45ef7347..b71637cfcb728 100644 --- a/frontend/src/scenes/insights/filters/TestAccountFilter/TestAccountFilter.tsx +++ b/frontend/src/scenes/insights/filters/TestAccountFilter/TestAccountFilter.tsx @@ -1,11 +1,12 @@ import { useActions, useValues } from 'kea' -import { FilterType } from '~/types' -import { teamLogic } from 'scenes/teamLogic' -import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconSettings } from 'lib/lemon-ui/icons' -import { urls } from 'scenes/urls' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { FilterType } from '~/types' export function TestAccountFilter({ filters, diff --git a/frontend/src/scenes/insights/insightCommandLogic.ts b/frontend/src/scenes/insights/insightCommandLogic.ts index e31060d552d85..12d70300e2a0a 100644 --- a/frontend/src/scenes/insights/insightCommandLogic.ts +++ b/frontend/src/scenes/insights/insightCommandLogic.ts @@ -1,11 +1,13 @@ +import { connect, events, kea, key, path, props } from 'kea' import { Command, commandPaletteLogic } from 'lib/components/CommandPalette/commandPaletteLogic' -import { kea, props, key, path, connect, events } from 'kea' -import type { insightCommandLogicType } from './insightCommandLogicType' +import { IconTrendingUp } from 'lib/lemon-ui/icons' import { dateMapping } from 'lib/utils' -import { InsightLogicProps } from '~/types' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { InsightLogicProps } from '~/types' + +import type { insightCommandLogicType } from './insightCommandLogicType' import { insightVizDataLogic } from './insightVizDataLogic' -import { IconTrendingUp } from 'lib/lemon-ui/icons' const INSIGHT_COMMAND_SCOPE = 'insights' diff --git a/frontend/src/scenes/insights/insightDataLogic.test.ts b/frontend/src/scenes/insights/insightDataLogic.test.ts index 52c99c600f902..acc5b30da8093 100644 --- a/frontend/src/scenes/insights/insightDataLogic.test.ts +++ b/frontend/src/scenes/insights/insightDataLogic.test.ts @@ -1,14 +1,14 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { insightLogic } from 'scenes/insights/insightLogic' +import { useMocks } from '~/mocks/jest' +import { examples } from '~/queries/examples' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { NodeKind, TrendsQuery } from '~/queries/schema' +import { initKeaTests } from '~/test/init' import { InsightShortId } from '~/types' import { insightDataLogic } from './insightDataLogic' -import { NodeKind, TrendsQuery } from '~/queries/schema' -import { useMocks } from '~/mocks/jest' -import { insightLogic } from 'scenes/insights/insightLogic' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { examples } from '~/queries/examples' const Insight123 = '123' as InsightShortId diff --git a/frontend/src/scenes/insights/insightDataLogic.ts b/frontend/src/scenes/insights/insightDataLogic.ts index 89e2dec570f32..a392c583e8c58 100644 --- a/frontend/src/scenes/insights/insightDataLogic.ts +++ b/frontend/src/scenes/insights/insightDataLogic.ts @@ -1,23 +1,24 @@ import { actions, connect, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' -import { FilterType, InsightLogicProps, InsightType } from '~/types' +import { objectsEqual } from 'lib/utils' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' +import { insightTypeToDefaultQuery, nodeKindToDefaultQuery } from '~/queries/nodes/InsightQuery/defaults' +import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' +import { queryExportContext } from '~/queries/query' import { InsightNodeKind, InsightVizNode, Node, NodeKind } from '~/queries/schema' +import { isInsightVizNode } from '~/queries/utils' +import { FilterType, InsightLogicProps, InsightType } from '~/types' import type { insightDataLogicType } from './insightDataLogicType' +import { insightDataTimingLogic } from './insightDataTimingLogic' import { insightLogic } from './insightLogic' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' -import { isInsightVizNode } from '~/queries/utils' import { cleanFilters, setTestAccountFilterForNewInsight } from './utils/cleanFilters' -import { insightTypeToDefaultQuery, nodeKindToDefaultQuery } from '~/queries/nodes/InsightQuery/defaults' -import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' -import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' -import { queryExportContext } from '~/queries/query' -import { objectsEqual } from 'lib/utils' import { compareFilters } from './utils/compareFilters' -import { insightDataTimingLogic } from './insightDataTimingLogic' -import { teamLogic } from 'scenes/teamLogic' -import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' const queryFromFilters = (filters: Partial): InsightVizNode => ({ kind: NodeKind.InsightVizNode, diff --git a/frontend/src/scenes/insights/insightDataTimingLogic.ts b/frontend/src/scenes/insights/insightDataTimingLogic.ts index 688c0bf4a7fba..22d6558d43b42 100644 --- a/frontend/src/scenes/insights/insightDataTimingLogic.ts +++ b/frontend/src/scenes/insights/insightDataTimingLogic.ts @@ -1,12 +1,13 @@ -import { kea, props, key, path, connect, listeners, reducers, actions } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers } from 'kea' +import { captureTimeToSeeData } from 'lib/internalMetrics' +import { teamLogic } from 'scenes/teamLogic' + import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' import { InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from './sharedUtils' import type { insightDataTimingLogicType } from './insightDataTimingLogicType' -import { teamLogic } from 'scenes/teamLogic' -import { captureTimeToSeeData } from 'lib/internalMetrics' +import { keyForInsightLogicProps } from './sharedUtils' export const insightDataTimingLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/scenes/insights/insightLogic.test.ts b/frontend/src/scenes/insights/insightLogic.test.ts index 038762fdfc77b..3856ef09974a3 100644 --- a/frontend/src/scenes/insights/insightLogic.test.ts +++ b/frontend/src/scenes/insights/insightLogic.test.ts @@ -1,6 +1,22 @@ +import { combineUrl, router } from 'kea-router' import { expectLogic, partial, truth } from 'kea-test-utils' +import api from 'lib/api' +import { MOCK_DEFAULT_TEAM, MOCK_TEAM_ID } from 'lib/api.mock' +import { DashboardPrivilegeLevel, DashboardRestrictionLevel } from 'lib/constants' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' +import { useAvailableFeatures } from '~/mocks/features' +import { useMocks } from '~/mocks/jest' +import { dashboardsModel } from '~/models/dashboardsModel' +import { insightsModel } from '~/models/insightsModel' +import { DataTableNode, NodeKind } from '~/queries/schema' import { initKeaTests } from '~/test/init' -import { createEmptyInsight, insightLogic } from './insightLogic' import { AnyPropertyFilter, AvailableFeature, @@ -18,22 +34,8 @@ import { PropertyGroupFilter, PropertyOperator, } from '~/types' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { combineUrl, router } from 'kea-router' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' -import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { teamLogic } from 'scenes/teamLogic' -import { urls } from 'scenes/urls' -import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' -import { useMocks } from '~/mocks/jest' -import { useAvailableFeatures } from '~/mocks/features' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' -import { MOCK_DEFAULT_TEAM, MOCK_TEAM_ID } from 'lib/api.mock' -import { dashboardsModel } from '~/models/dashboardsModel' -import { insightsModel } from '~/models/insightsModel' -import { DashboardPrivilegeLevel, DashboardRestrictionLevel } from 'lib/constants' -import api from 'lib/api' -import { DataTableNode, NodeKind } from '~/queries/schema' + +import { createEmptyInsight, insightLogic } from './insightLogic' const API_FILTERS: Partial = { insight: InsightType.TRENDS as InsightType, diff --git a/frontend/src/scenes/insights/insightLogic.ts b/frontend/src/scenes/insights/insightLogic.ts index 825d1a1237265..75649d9bcbde3 100644 --- a/frontend/src/scenes/insights/insightLogic.ts +++ b/frontend/src/scenes/insights/insightLogic.ts @@ -1,22 +1,17 @@ -import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { promptLogic } from 'lib/logic/promptLogic' -import { getEventNamesForAction, objectsEqual, sum, toParams } from 'lib/utils' -import { eventUsageLogic, InsightEventSource } from 'lib/utils/eventUsageLogic' -import type { insightLogicType } from './insightLogicType' import { captureException } from '@sentry/react' -import { - ActionType, - FilterType, - InsightLogicProps, - InsightModel, - InsightShortId, - ItemMode, - SetInsightOptions, - TrendsFilterType, -} from '~/types' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { router } from 'kea-router' import api from 'lib/api' +import { TriggerExportProps } from 'lib/components/ExportButton/exporter' +import { parseProperties } from 'lib/components/PropertyFilters/utils' +import { DashboardPrivilegeLevel } from 'lib/constants' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { promptLogic } from 'lib/logic/promptLogic' +import { getEventNamesForAction, objectsEqual, sum, toParams } from 'lib/utils' +import { eventUsageLogic, InsightEventSource } from 'lib/utils/eventUsageLogic' +import { transformLegacyHiddenLegendKeys } from 'scenes/funnels/funnelUtils' +import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' import { filterTrendsClientSideParams, isFilterWithHiddenLegendKeys, @@ -28,30 +23,37 @@ import { isTrendsFilter, keyForInsightLogicProps, } from 'scenes/insights/sharedUtils' +import { summarizeInsight } from 'scenes/insights/summarizeInsight' import { cleanFilters } from 'scenes/insights/utils/cleanFilters' -import { dashboardsModel } from '~/models/dashboardsModel' -import { extractObjectDiffKeys, findInsightFromMountedLogic, getInsightId } from './utils' -import { teamLogic } from '../teamLogic' import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' +import { mathsLogic } from 'scenes/trends/mathsLogic' import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + import { actionsModel } from '~/models/actionsModel' -import { DashboardPrivilegeLevel } from 'lib/constants' -import { groupsModel } from '~/models/groupsModel' import { cohortsModel } from '~/models/cohortsModel' -import { mathsLogic } from 'scenes/trends/mathsLogic' -import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { TriggerExportProps } from 'lib/components/ExportButton/exporter' -import { parseProperties } from 'lib/components/PropertyFilters/utils' +import { dashboardsModel } from '~/models/dashboardsModel' +import { groupsModel } from '~/models/groupsModel' import { insightsModel } from '~/models/insightsModel' -import { toLocalFilters } from './filters/ActionFilter/entityFilterLogic' -import { loaders } from 'kea-loaders' -import { queryExportContext } from '~/queries/query' import { tagsModel } from '~/models/tagsModel' -import { isInsightVizNode } from '~/queries/utils' -import { userLogic } from 'scenes/userLogic' -import { transformLegacyHiddenLegendKeys } from 'scenes/funnels/funnelUtils' -import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { queryExportContext } from '~/queries/query' import { InsightVizNode } from '~/queries/schema' +import { isInsightVizNode } from '~/queries/utils' +import { + ActionType, + FilterType, + InsightLogicProps, + InsightModel, + InsightShortId, + ItemMode, + SetInsightOptions, + TrendsFilterType, +} from '~/types' + +import { teamLogic } from '../teamLogic' +import { toLocalFilters } from './filters/ActionFilter/entityFilterLogic' +import type { insightLogicType } from './insightLogicType' +import { extractObjectDiffKeys, findInsightFromMountedLogic, getInsightId } from './utils' const IS_TEST_MODE = process.env.NODE_ENV === 'test' export const UNSAVED_INSIGHT_MIN_REFRESH_INTERVAL_MINUTES = 3 diff --git a/frontend/src/scenes/insights/insightSceneLogic.test.ts b/frontend/src/scenes/insights/insightSceneLogic.test.ts index 1d8b623a087c2..1d3b68fa2300d 100644 --- a/frontend/src/scenes/insights/insightSceneLogic.test.ts +++ b/frontend/src/scenes/insights/insightSceneLogic.test.ts @@ -1,10 +1,11 @@ import { combineUrl, router } from 'kea-router' -import { urls } from 'scenes/urls' import { expectLogic, partial } from 'kea-test-utils' -import { InsightShortId, InsightType, ItemMode } from '~/types' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { initKeaTests } from '~/test/init' +import { urls } from 'scenes/urls' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { InsightShortId, InsightType, ItemMode } from '~/types' const Insight12 = '12' as InsightShortId const Insight42 = '42' as InsightShortId diff --git a/frontend/src/scenes/insights/insightSceneLogic.tsx b/frontend/src/scenes/insights/insightSceneLogic.tsx index e626919fe5c95..eea9fe3ca0279 100644 --- a/frontend/src/scenes/insights/insightSceneLogic.tsx +++ b/frontend/src/scenes/insights/insightSceneLogic.tsx @@ -1,19 +1,21 @@ import { actions, BuiltLogic, connect, kea, listeners, path, reducers, selectors, sharedListeners } from 'kea' -import { Breadcrumb, FilterType, InsightShortId, InsightType, ItemMode } from '~/types' -import { eventUsageLogic, InsightEventSource } from 'lib/utils/eventUsageLogic' import { actionToUrl, beforeUnload, router, urlToAction } from 'kea-router' -import type { insightSceneLogicType } from './insightSceneLogicType' -import { urls } from 'scenes/urls' -import { insightLogicType } from 'scenes/insights/insightLogicType' -import { createEmptyInsight, insightLogic } from 'scenes/insights/insightLogic' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { eventUsageLogic, InsightEventSource } from 'lib/utils/eventUsageLogic' +import { createEmptyInsight, insightLogic } from 'scenes/insights/insightLogic' +import { insightLogicType } from 'scenes/insights/insightLogicType' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { Breadcrumb, FilterType, InsightShortId, InsightType, ItemMode } from '~/types' + import { insightDataLogic } from './insightDataLogic' import { insightDataLogicType } from './insightDataLogicType' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import type { insightSceneLogicType } from './insightSceneLogicType' export const insightSceneLogic = kea([ path(['scenes', 'insights', 'insightSceneLogic']), diff --git a/frontend/src/scenes/insights/insightVizDataLogic.test.ts b/frontend/src/scenes/insights/insightVizDataLogic.test.ts index 260b7dcd112f2..065160e2f2c88 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.test.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.test.ts @@ -1,16 +1,16 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { FunnelLayout } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { funnelInvalidExclusionError, funnelResult } from 'scenes/funnels/__mocks__/funnelDataLogicMocks' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { useMocks } from '~/mocks/jest' +import { funnelsQueryDefault, trendsQueryDefault } from '~/queries/nodes/InsightQuery/defaults' +import { ActionsNode, EventsNode, FunnelsQuery, InsightQueryNode, NodeKind, TrendsQuery } from '~/queries/schema' +import { initKeaTests } from '~/test/init' import { BaseMathType, ChartDisplayType, InsightModel, InsightShortId, InsightType } from '~/types' import { insightDataLogic } from './insightDataLogic' -import { useMocks } from '~/mocks/jest' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { trendsQueryDefault, funnelsQueryDefault } from '~/queries/nodes/InsightQuery/defaults' -import { ActionsNode, EventsNode, FunnelsQuery, InsightQueryNode, NodeKind, TrendsQuery } from '~/queries/schema' -import { FunnelLayout } from 'lib/constants' -import { funnelInvalidExclusionError, funnelResult } from 'scenes/funnels/__mocks__/funnelDataLogicMocks' const Insight123 = '123' as InsightShortId diff --git a/frontend/src/scenes/insights/insightVizDataLogic.ts b/frontend/src/scenes/insights/insightVizDataLogic.ts index 4b9c09fb8bc00..7a0b2a85f8079 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.ts @@ -1,15 +1,35 @@ -import posthog from 'posthog-js' -import { actions, connect, kea, key, listeners, path, props, selectors, reducers } from 'kea' +import { lemonToast } from '@posthog/lemon-ui' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { DISPLAY_TYPES_WITHOUT_LEGEND } from 'lib/components/InsightLegend/utils' +import { Intervals, intervals } from 'lib/components/IntervalFilter/intervals' +import { parseProperties } from 'lib/components/PropertyFilters/utils' import { - BaseMathType, - ChartDisplayType, - FilterType, - FunnelExclusion, - InsightLogicProps, - IntervalType, - TrendsFilterType, -} from '~/types' + NON_TIME_SERIES_DISPLAY_TYPES, + NON_VALUES_ON_SERIES_DISPLAY_TYPES, + PERCENT_STACK_VIEW_DISPLAY_TYPE, +} from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { dateMapping } from 'lib/utils' +import posthog from 'posthog-js' +import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { sceneLogic } from 'scenes/sceneLogic' +import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' +import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' + +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { + getBreakdown, + getCompare, + getDisplay, + getFormula, + getInterval, + getSeries, + getShowLabelsOnSeries, + getShowLegend, + getShowPercentStackView, + getShowValueOnSeries, +} from '~/queries/nodes/InsightViz/utils' import { BreakdownFilter, DateRange, @@ -21,9 +41,6 @@ import { TrendsFilter, TrendsQuery, } from '~/queries/schema' - -import { insightLogic } from './insightLogic' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { filterForQuery, filterKeyForQuery, @@ -39,34 +56,17 @@ import { nodeKindToFilterProperty, } from '~/queries/utils' import { - NON_TIME_SERIES_DISPLAY_TYPES, - NON_VALUES_ON_SERIES_DISPLAY_TYPES, - PERCENT_STACK_VIEW_DISPLAY_TYPE, -} from 'lib/constants' -import { - getBreakdown, - getCompare, - getDisplay, - getFormula, - getInterval, - getSeries, - getShowLegend, - getShowPercentStackView, - getShowValueOnSeries, -} from '~/queries/nodes/InsightViz/utils' -import { DISPLAY_TYPES_WITHOUT_LEGEND } from 'lib/components/InsightLegend/utils' -import { Intervals, intervals } from 'lib/components/IntervalFilter/intervals' -import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' - -import { sceneLogic } from 'scenes/sceneLogic' + BaseMathType, + ChartDisplayType, + FilterType, + FunnelExclusion, + InsightLogicProps, + IntervalType, + TrendsFilterType, +} from '~/types' +import { insightLogic } from './insightLogic' import type { insightVizDataLogicType } from './insightVizDataLogicType' -import { parseProperties } from 'lib/components/PropertyFilters/utils' -import { filterTestAccountsDefaultsLogic } from 'scenes/settings/project/filterTestAccountDefaultsLogic' -import { BASE_MATH_DEFINITIONS } from 'scenes/trends/mathsLogic' -import { lemonToast } from '@posthog/lemon-ui' -import { dayjs } from 'lib/dayjs' -import { dateMapping } from 'lib/utils' const SHOW_TIMEOUT_MESSAGE_AFTER = 5000 @@ -167,9 +167,9 @@ export const insightVizDataLogic = kea([ samplingFactor: [(s) => [s.querySource], (q) => (q ? q.samplingFactor : null)], showLegend: [(s) => [s.querySource], (q) => (q ? getShowLegend(q) : null)], showValueOnSeries: [(s) => [s.querySource], (q) => (q ? getShowValueOnSeries(q) : null)], + showLabelOnSeries: [(s) => [s.querySource], (q) => (q ? getShowLabelsOnSeries(q) : null)], showPercentStackView: [(s) => [s.querySource], (q) => (q ? getShowPercentStackView(q) : null)], vizSpecificOptions: [(s) => [s.query], (q: Node) => (isInsightVizNode(q) ? q.vizSpecificOptions : null)], - insightFilter: [(s) => [s.querySource], (q) => (q ? filterForQuery(q) : null)], trendsFilter: [(s) => [s.querySource], (q) => (isTrendsQuery(q) ? q.trendsFilter : null)], funnelsFilter: [(s) => [s.querySource], (q) => (isFunnelsQuery(q) ? q.funnelsFilter : null)], diff --git a/frontend/src/scenes/insights/sharedUtils.test.ts b/frontend/src/scenes/insights/sharedUtils.test.ts index 7c3e9a4000660..610d918bd5171 100644 --- a/frontend/src/scenes/insights/sharedUtils.test.ts +++ b/frontend/src/scenes/insights/sharedUtils.test.ts @@ -1,4 +1,5 @@ import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + import { InsightShortId } from '~/types' const Insight123 = '123' as InsightShortId diff --git a/frontend/src/scenes/insights/sharedUtils.ts b/frontend/src/scenes/insights/sharedUtils.ts index fa0c33b799745..7dcc6e3c105c4 100644 --- a/frontend/src/scenes/insights/sharedUtils.ts +++ b/frontend/src/scenes/insights/sharedUtils.ts @@ -1,6 +1,7 @@ // This is separate from utils.ts because here we don't include `funnelLogic`, `retentionLogic`, etc import { + ChartDisplayType, FilterType, FunnelsFilterType, InsightLogicProps, @@ -10,7 +11,6 @@ import { RetentionFilterType, StickinessFilterType, TrendsFilterType, - ChartDisplayType, } from '~/types' /** diff --git a/frontend/src/scenes/insights/summarizeInsight.test.ts b/frontend/src/scenes/insights/summarizeInsight.test.ts index 994a38cddd821..8bc060941642f 100644 --- a/frontend/src/scenes/insights/summarizeInsight.test.ts +++ b/frontend/src/scenes/insights/summarizeInsight.test.ts @@ -1,17 +1,5 @@ -import { Noun } from '~/models/groupsModel' -import { - BaseMathType, - CohortType, - CountPerActorMathType, - FilterLogicalOperator, - GroupMathType, - InsightType, - PathsFilterType, - PathType, - PropertyMathType, - RetentionFilterType, - TrendsFilterType, -} from '~/types' +import { RETENTION_FIRST_TIME, RETENTION_RECURRING } from 'lib/constants' +import { summarizeInsight, SummaryContext } from 'scenes/insights/summarizeInsight' import { BASE_MATH_DEFINITIONS, COUNT_PER_ACTOR_MATH_DEFINITIONS, @@ -20,7 +8,8 @@ import { MathDefinition, PROPERTY_MATH_DEFINITIONS, } from 'scenes/trends/mathsLogic' -import { RETENTION_FIRST_TIME, RETENTION_RECURRING } from 'lib/constants' + +import { Noun } from '~/models/groupsModel' import { DataTableNode, FunnelsQuery, @@ -33,7 +22,19 @@ import { TimeToSeeDataWaterfallNode, TrendsQuery, } from '~/queries/schema' -import { summarizeInsight, SummaryContext } from 'scenes/insights/summarizeInsight' +import { + BaseMathType, + CohortType, + CountPerActorMathType, + FilterLogicalOperator, + GroupMathType, + InsightType, + PathsFilterType, + PathType, + PropertyMathType, + RetentionFilterType, + TrendsFilterType, +} from '~/types' const aggregationLabel = (groupTypeIndex: number | null | undefined): Noun => groupTypeIndex != undefined diff --git a/frontend/src/scenes/insights/summarizeInsight.ts b/frontend/src/scenes/insights/summarizeInsight.ts index 2efd49cf79677..a0f4aca384486 100644 --- a/frontend/src/scenes/insights/summarizeInsight.ts +++ b/frontend/src/scenes/insights/summarizeInsight.ts @@ -1,6 +1,6 @@ -import { AnyPartialFilterType, EntityFilter, FilterType, FunnelVizType, StepOrderValue } from '~/types' -import { BreakdownFilter, InsightQueryNode, Node } from '~/queries/schema' +import { RETENTION_FIRST_TIME } from 'lib/constants' import { KEY_MAPPING } from 'lib/taxonomy' +import { alphabet, capitalizeFirstLetter } from 'lib/utils' import { toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' import { isFunnelsFilter, @@ -10,10 +10,19 @@ import { isStickinessFilter, isTrendsFilter, } from 'scenes/insights/sharedUtils' +import { + getDisplayNameFromEntityFilter, + getDisplayNameFromEntityNode, + humanizePathsEventTypes, +} from 'scenes/insights/utils' import { retentionOptions } from 'scenes/retention/constants' -import { RETENTION_FIRST_TIME } from 'lib/constants' -import { alphabet, capitalizeFirstLetter } from 'lib/utils' import { apiValueToMathType, MathCategory, MathDefinition } from 'scenes/trends/mathsLogic' +import { mathsLogicType } from 'scenes/trends/mathsLogicType' + +import { cohortsModelType } from '~/models/cohortsModelType' +import { groupsModelType } from '~/models/groupsModelType' +import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' +import { BreakdownFilter, InsightQueryNode, Node } from '~/queries/schema' import { isDataTableNode, isEventsQuery, @@ -29,15 +38,7 @@ import { isTimeToSeeDataSessionsQuery, isTrendsQuery, } from '~/queries/utils' -import { groupsModelType } from '~/models/groupsModelType' -import { cohortsModelType } from '~/models/cohortsModelType' -import { mathsLogicType } from 'scenes/trends/mathsLogicType' -import { - getDisplayNameFromEntityFilter, - getDisplayNameFromEntityNode, - humanizePathsEventTypes, -} from 'scenes/insights/utils' -import { extractExpressionComment } from '~/queries/nodes/DataTable/utils' +import { AnyPartialFilterType, EntityFilter, FilterType, FunnelVizType, StepOrderValue } from '~/types' function summarizeBreakdown(filters: Partial | BreakdownFilter, context: SummaryContext): string | null { const { breakdown_type, breakdown, breakdown_group_type_index } = filters diff --git a/frontend/src/scenes/insights/utils.test.ts b/frontend/src/scenes/insights/utils.test.ts index 441e69d0e5967..94bad45af4386 100644 --- a/frontend/src/scenes/insights/utils.test.ts +++ b/frontend/src/scenes/insights/utils.test.ts @@ -1,4 +1,4 @@ -import { Entity, EntityFilter, FilterType, InsightType } from '~/types' +import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' import { extractObjectDiffKeys, formatAggregationValue, @@ -7,9 +7,10 @@ import { getDisplayNameFromEntityFilter, getDisplayNameFromEntityNode, } from 'scenes/insights/utils' -import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' + import { ActionsNode, BreakdownFilter, EventsNode, NodeKind } from '~/queries/schema' import { isEventsNode } from '~/queries/utils' +import { Entity, EntityFilter, FilterType, InsightType } from '~/types' const createFilter = (id?: Entity['id'], name?: string, custom_name?: string): EntityFilter => { return { diff --git a/frontend/src/scenes/insights/utils.tsx b/frontend/src/scenes/insights/utils.tsx index 26ad7a3a77a8d..35e2b04b251f8 100644 --- a/frontend/src/scenes/insights/utils.tsx +++ b/frontend/src/scenes/insights/utils.tsx @@ -1,3 +1,18 @@ +import api from 'lib/api' +import { dayjs } from 'lib/dayjs' +import { KEY_MAPPING } from 'lib/taxonomy' +import { ensureStringIsNotBlank, humanFriendlyNumber, objectsEqual } from 'lib/utils' +import { getCurrentTeamId } from 'lib/utils/logics' +import { ReactNode } from 'react' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' +import { urls } from 'scenes/urls' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { FormatPropertyValueForDisplayFunction } from '~/models/propertyDefinitionsModel' +import { examples } from '~/queries/examples' +import { ActionsNode, BreakdownFilter, EventsNode } from '~/queries/schema' +import { isEventsNode } from '~/queries/utils' import { ActionFilter, AnyPartialFilterType, @@ -15,21 +30,8 @@ import { PathType, TrendsFilterType, } from '~/types' -import { ensureStringIsNotBlank, humanFriendlyNumber, objectsEqual } from 'lib/utils' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' -import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { KEY_MAPPING } from 'lib/taxonomy' -import api from 'lib/api' -import { dayjs } from 'lib/dayjs' -import { getCurrentTeamId } from 'lib/utils/logics' -import { dashboardsModel } from '~/models/dashboardsModel' + import { insightLogic } from './insightLogic' -import { FormatPropertyValueForDisplayFunction } from '~/models/propertyDefinitionsModel' -import { ReactNode } from 'react' -import { ActionsNode, BreakdownFilter, EventsNode } from '~/queries/schema' -import { isEventsNode } from '~/queries/utils' -import { urls } from 'scenes/urls' -import { examples } from '~/queries/examples' export const isAllEventsEntityFilter = (filter: EntityFilter | ActionFilter | null): boolean => { return ( diff --git a/frontend/src/scenes/insights/utils/cleanFilters.test.ts b/frontend/src/scenes/insights/utils/cleanFilters.test.ts index f3cb5e2495157..d49c8d03ff1cf 100644 --- a/frontend/src/scenes/insights/utils/cleanFilters.test.ts +++ b/frontend/src/scenes/insights/utils/cleanFilters.test.ts @@ -1,4 +1,5 @@ -import { cleanFilters } from './cleanFilters' +import { NON_VALUES_ON_SERIES_DISPLAY_TYPES, ShownAsValue } from 'lib/constants' + import { ChartDisplayType, FilterType, @@ -8,7 +9,8 @@ import { InsightType, TrendsFilterType, } from '~/types' -import { NON_VALUES_ON_SERIES_DISPLAY_TYPES, ShownAsValue } from 'lib/constants' + +import { cleanFilters } from './cleanFilters' describe('cleanFilters', () => { it('removes shownas from trends insights', () => { diff --git a/frontend/src/scenes/insights/utils/cleanFilters.ts b/frontend/src/scenes/insights/utils/cleanFilters.ts index fced598e698a0..2d5a7c12582a5 100644 --- a/frontend/src/scenes/insights/utils/cleanFilters.ts +++ b/frontend/src/scenes/insights/utils/cleanFilters.ts @@ -1,3 +1,25 @@ +import { smoothingOptions } from 'lib/components/SmoothingFilter/smoothings' +import { + BIN_COUNT_AUTO, + NON_TIME_SERIES_DISPLAY_TYPES, + NON_VALUES_ON_SERIES_DISPLAY_TYPES, + PERCENT_STACK_VIEW_DISPLAY_TYPE, + RETENTION_FIRST_TIME, + ShownAsValue, +} from 'lib/constants' +import { getDefaultEventName } from 'lib/utils/getAppContext' +import { deepCleanFunnelExclusionEvents, getClampedStepRangeFilter, isStepsUndefined } from 'scenes/funnels/funnelUtils' +import { isURLNormalizeable } from 'scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterUtils' +import { + isFunnelsFilter, + isLifecycleFilter, + isPathsFilter, + isRetentionFilter, + isStickinessFilter, + isTrendsFilter, +} from 'scenes/insights/sharedUtils' +import { DEFAULT_STEP_LIMIT } from 'scenes/paths/pathsDataLogic' + import { AnyFilterType, ChartDisplayType, @@ -16,28 +38,8 @@ import { StickinessFilterType, TrendsFilterType, } from '~/types' -import { deepCleanFunnelExclusionEvents, getClampedStepRangeFilter, isStepsUndefined } from 'scenes/funnels/funnelUtils' -import { getDefaultEventName } from 'lib/utils/getAppContext' -import { - BIN_COUNT_AUTO, - NON_TIME_SERIES_DISPLAY_TYPES, - NON_VALUES_ON_SERIES_DISPLAY_TYPES, - PERCENT_STACK_VIEW_DISPLAY_TYPE, - RETENTION_FIRST_TIME, - ShownAsValue, -} from 'lib/constants' -import { DEFAULT_STEP_LIMIT } from 'scenes/paths/pathsDataLogic' -import { smoothingOptions } from 'lib/components/SmoothingFilter/smoothings' + import { LocalFilter, toLocalFilters } from '../filters/ActionFilter/entityFilterLogic' -import { - isFunnelsFilter, - isLifecycleFilter, - isPathsFilter, - isRetentionFilter, - isStickinessFilter, - isTrendsFilter, -} from 'scenes/insights/sharedUtils' -import { isURLNormalizeable } from 'scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterUtils' export function getDefaultEvent(): Entity { const event = getDefaultEventName() diff --git a/frontend/src/scenes/insights/utils/compareFilters.ts b/frontend/src/scenes/insights/utils/compareFilters.ts index e2ccf3c14ba97..93afde2296b6b 100644 --- a/frontend/src/scenes/insights/utils/compareFilters.ts +++ b/frontend/src/scenes/insights/utils/compareFilters.ts @@ -1,6 +1,7 @@ -import { AnyFilterType } from '~/types' import { objectCleanWithEmpty, objectsEqual } from 'lib/utils' +import { AnyFilterType } from '~/types' + import { cleanFilters } from './cleanFilters' /** clean filters so that we can check for semantic equality with a deep equality check */ diff --git a/frontend/src/scenes/insights/utils/compareInsightQuery.ts b/frontend/src/scenes/insights/utils/compareInsightQuery.ts index 95039cabecf93..ab3ca5962722c 100644 --- a/frontend/src/scenes/insights/utils/compareInsightQuery.ts +++ b/frontend/src/scenes/insights/utils/compareInsightQuery.ts @@ -1,6 +1,6 @@ -import { InsightQueryNode } from '~/queries/schema' - import { objectCleanWithEmpty, objectsEqual } from 'lib/utils' + +import { InsightQueryNode } from '~/queries/schema' import { filterForQuery, filterKeyForQuery, diff --git a/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.scss b/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.scss index 0691356cacf30..510dc8b1bceb4 100644 --- a/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.scss +++ b/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.scss @@ -2,42 +2,30 @@ .BoldNumber { width: 100%; - padding: 2rem 0 3rem; + padding: 2rem 3rem 3rem; display: flex; align-items: center; justify-content: center; flex-direction: column; flex: 1; - @include screen($md) { - padding: 3rem 0 5rem; - - .InsightCard & { - padding: 2rem 0; - } - } -} - -.BoldNumber__value { - width: 100%; - text-align: center; - padding: 0 2rem; - margin: 0 auto; - line-height: 1; - font-weight: 700; - letter-spacing: -0.025em; - .InsightCard & { - padding: 0 1rem; + padding: 1rem; } @include screen($md) { - padding: 0 5rem; + padding: 3rem 5rem 5rem; .InsightCard & { - padding: 0 2rem; + padding: 2rem; } } + + .BoldNumber__value { + font-weight: 700; + width: 100%; + letter-spacing: -0.025em; + } } .BoldNumber__comparison { diff --git a/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.tsx b/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.tsx index c3f0156684166..f4b79b2a4c8b7 100644 --- a/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.tsx +++ b/frontend/src/scenes/insights/views/BoldNumber/BoldNumber.tsx @@ -1,25 +1,27 @@ -import { useValues } from 'kea' -import { useLayoutEffect, useRef, useState } from 'react' -import clsx from 'clsx' - -import { insightLogic } from '../../insightLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import './BoldNumber.scss' +import './BoldNumber.scss' -import { ChartParams, TrendResult } from '~/types' -import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' -import { ensureTooltip } from '../LineGraph/LineGraph' -import { groupsModel } from '~/models/groupsModel' -import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' -import { IconFlare, IconTrendingDown, IconTrendingFlat, IconTrendingUp } from 'lib/lemon-ui/icons' import { LemonRow, Link } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { useValues } from 'kea' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { IconFlare, IconTrendingDown, IconTrendingFlat, IconTrendingUp } from 'lib/lemon-ui/icons' import { percentage } from 'lib/utils' +import { useLayoutEffect, useRef, useState } from 'react' +import { useEffect } from 'react' +import React from 'react' +import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' import { InsightEmptyState } from 'scenes/insights/EmptyStates' +import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import './BoldNumber.scss' -import { useEffect } from 'react' -import Textfit from './Textfit' +import { groupsModel } from '~/models/groupsModel' +import { ChartParams, TrendResult } from '~/types' + +import { insightLogic } from '../../insightLogic' +import { ensureTooltip } from '../LineGraph/LineGraph' +import { Textfit } from './Textfit' /** The tooltip is offset by a few pixels from the cursor to give it some breathing room. */ const BOLD_NUMBER_TOOLTIP_OFFSET_PX = 8 @@ -94,29 +96,29 @@ export function BoldNumber({ showPersonsModal = true }: ChartParams): JSX.Elemen return resultSeries ? (
    - -
    { - if (resultSeries.persons?.url) { - openPersonsModal({ - url: resultSeries.persons?.url, - title: , - }) - } +
    { + if (resultSeries.persons?.url) { + openPersonsModal({ + url: resultSeries.persons?.url, + title: , + }) } - : undefined - } - onMouseLeave={() => setIsTooltipShown(false)} - ref={valueRef} - onMouseEnter={() => setIsTooltipShown(true)} - > + } + : undefined + } + onMouseLeave={() => setIsTooltipShown(false)} + ref={valueRef} + onMouseEnter={() => setIsTooltipShown(true)} + > + {formatAggregationAxisValue(trendsFilter, resultSeries.aggregated_value)} -
    - + +
    {showComparison && }
    ) : ( diff --git a/frontend/src/scenes/insights/views/BoldNumber/Textfit.stories.tsx b/frontend/src/scenes/insights/views/BoldNumber/Textfit.stories.tsx new file mode 100644 index 0000000000000..69d3a534095dd --- /dev/null +++ b/frontend/src/scenes/insights/views/BoldNumber/Textfit.stories.tsx @@ -0,0 +1,26 @@ +import { Meta, StoryFn, StoryObj } from '@storybook/react' + +import { Textfit } from './Textfit' + +type Story = StoryObj +const meta: Meta = { + title: 'Lemon UI/TextFit', + component: Textfit, + tags: ['autodocs'], + args: { + min: 20, + max: 150, + children: '10000000', + }, +} +export default meta + +const Template: StoryFn = (props) => { + return ( +
    + +
    + ) +} + +export const Basic: Story = Template.bind({}) diff --git a/frontend/src/scenes/insights/views/BoldNumber/Textfit.tsx b/frontend/src/scenes/insights/views/BoldNumber/Textfit.tsx index 8d05cea3b4e59..b76f20f920ea3 100644 --- a/frontend/src/scenes/insights/views/BoldNumber/Textfit.tsx +++ b/frontend/src/scenes/insights/views/BoldNumber/Textfit.tsx @@ -1,83 +1,77 @@ -// Adapted from https://github.com/malte-wessel/react-textfit -// which is no longer maintained and does not support React 18 +import { useRef } from 'react' +import useResizeObserver from 'use-resize-observer' -import { useEffect, useRef, useState } from 'react' - -// Calculate width without padding. -const innerWidth = (el: HTMLDivElement): number => { - const style = window.getComputedStyle(el, null) - // Hidden iframe in Firefox returns null, https://github.com/malte-wessel/react-textfit/pull/34 - if (!style) { - return el.clientWidth - } - - return ( - el.clientWidth - - parseInt(style.getPropertyValue('padding-left'), 10) - - parseInt(style.getPropertyValue('padding-right'), 10) - ) +export type TextfitProps = { + min: number + max: number + children: string } -const assertElementFitsWidth = (el: HTMLDivElement, width: number): boolean => el.scrollWidth - 1 <= width - -const Textfit = ({ min, max, children }: { min: number; max: number; children: React.ReactNode }): JSX.Element => { +export const Textfit = ({ min, max, children }: TextfitProps): JSX.Element => { const parentRef = useRef(null) const childRef = useRef(null) - - const [fontSize, setFontSize] = useState() + const fontSizeRef = useRef(min) let resizeTimer: NodeJS.Timeout - const handleWindowResize = (): void => { + const updateFontSize = (size: number): void => { + fontSizeRef.current = size + childRef.current!.style.fontSize = `${size}px` + } + + const handleResize = (): void => { clearTimeout(resizeTimer) resizeTimer = setTimeout(() => { - const el = parentRef.current - const wrapper = childRef.current + const parent = parentRef.current + const child = childRef.current + + if (!parent || !child) { + return + } - if (el && wrapper) { - const originalWidth = innerWidth(el) + let mid + let low = min + let high = max - let mid - let low = min - let high = max + while (low <= high) { + mid = Math.floor((low + high) / 2) + updateFontSize(mid) + const childRect = child.getBoundingClientRect() + const parentRect = parent.getBoundingClientRect() - while (low <= high) { - mid = Math.floor((low + high) / 2) - setFontSize(mid) + const childFitsParent = childRect.width <= parentRect.width && childRect.height <= parentRect.height - if (assertElementFitsWidth(wrapper, originalWidth)) { - low = mid + 1 - } else { - high = mid - 1 - } + if (childFitsParent) { + low = mid + 1 + } else { + high = mid - 1 } - mid = Math.min(low, high) + } + mid = Math.min(low, high) - // Ensure we hit the user-supplied limits - mid = Math.max(mid, min) - mid = Math.min(mid, max) + // Ensure we hit the user-supplied limits + mid = Math.max(mid, min) + mid = Math.min(mid, max) - setFontSize(mid) - } - }, 10) + updateFontSize(mid) + }, 50) } - useEffect(() => { - window.addEventListener('resize', handleWindowResize) - return () => window.removeEventListener('resize', handleWindowResize) - }, []) - - useEffect(() => handleWindowResize(), [parentRef, childRef]) + useResizeObserver({ + ref: parentRef, + onResize: () => handleResize(), + }) return ( - // eslint-disable-next-line react/forbid-dom-props -
    - {/* eslint-disable-next-line react/forbid-dom-props */} -
    +
    +
    {children}
    ) } - -export default Textfit diff --git a/frontend/src/scenes/insights/views/Funnels/CorrelationActionsCell.tsx b/frontend/src/scenes/insights/views/Funnels/CorrelationActionsCell.tsx index bd4b4dcaba5ba..f346115de4a0e 100644 --- a/frontend/src/scenes/insights/views/Funnels/CorrelationActionsCell.tsx +++ b/frontend/src/scenes/insights/views/Funnels/CorrelationActionsCell.tsx @@ -1,15 +1,14 @@ -import { useState } from 'react' import { useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic' +import { IconEllipsis } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { useState } from 'react' import { funnelCorrelationDetailsLogic } from 'scenes/funnels/funnelCorrelationDetailsLogic' +import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic' import { funnelPropertyCorrelationLogic } from 'scenes/funnels/funnelPropertyCorrelationLogic' +import { insightLogic } from 'scenes/insights/insightLogic' import { FunnelCorrelation, FunnelCorrelationResultsType } from '~/types' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import { IconEllipsis } from 'lib/lemon-ui/icons' export const EventCorrelationActionsCell = ({ record }: { record: FunnelCorrelation }): JSX.Element => { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/CorrelationMatrix.tsx b/frontend/src/scenes/insights/views/Funnels/CorrelationMatrix.tsx index 4506930a29828..9e8a922f250f3 100644 --- a/frontend/src/scenes/insights/views/Funnels/CorrelationMatrix.tsx +++ b/frontend/src/scenes/insights/views/Funnels/CorrelationMatrix.tsx @@ -1,13 +1,9 @@ -import { Button, Modal } from 'antd' import './CorrelationMatrix.scss' + +import { Button, Modal } from 'antd' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { insightLogic } from 'scenes/insights/insightLogic' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { capitalizeFirstLetter, percentage, pluralize } from 'lib/utils' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { Link } from 'lib/lemon-ui/Link' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' import { IconCancel, IconCheckCircleOutline, @@ -16,11 +12,17 @@ import { IconTrendingFlatDown, } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import clsx from 'clsx' -import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' -import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic' +import { Link } from 'lib/lemon-ui/Link' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter, percentage, pluralize } from 'lib/utils' import { funnelCorrelationDetailsLogic } from 'scenes/funnels/funnelCorrelationDetailsLogic' +import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic' import { funnelPersonsModalLogic } from 'scenes/funnels/funnelPersonsModalLogic' +import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' +import { insightLogic } from 'scenes/insights/insightLogic' + +import { FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' export function CorrelationMatrix(): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelBinsPicker.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelBinsPicker.tsx index a3971c891a7de..b10dd04e1e45a 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelBinsPicker.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelBinsPicker.tsx @@ -1,12 +1,13 @@ -import { useActions, useValues } from 'kea' -import { BIN_COUNT_AUTO } from 'lib/constants' import { InputNumber, Select } from 'antd' -import { BinCountValue } from '~/types' import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { BIN_COUNT_AUTO } from 'lib/constants' +import { IconBarChart } from 'lib/lemon-ui/icons' import { ANTD_TOOLTIP_PLACEMENTS } from 'lib/utils' -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { IconBarChart } from 'lib/lemon-ui/icons' +import { insightLogic } from 'scenes/insights/insightLogic' + +import { BinCountValue } from '~/types' // Constraints as defined in funnel_time_to_convert.py:34 const MIN = 1 diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelConversionWindowFilter.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelConversionWindowFilter.tsx index 59deda6801480..e938e03c69eb6 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelConversionWindowFilter.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelConversionWindowFilter.tsx @@ -1,13 +1,14 @@ -import { capitalizeFirstLetter, pluralize } from 'lib/utils' -import { useState } from 'react' -import { EditorFilterProps, FunnelConversionWindow, FunnelConversionWindowTimeUnit } from '~/types' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { useDebouncedCallback } from 'use-debounce' +import { IconInfo } from '@posthog/icons' import { LemonInput, LemonSelect, LemonSelectOption } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter, pluralize } from 'lib/utils' +import { useState } from 'react' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { useDebouncedCallback } from 'use-debounce' + import { FunnelsFilter } from '~/queries/schema' -import { IconInfo } from '@posthog/icons' +import { EditorFilterProps, FunnelConversionWindow, FunnelConversionWindowTimeUnit } from '~/types' const TIME_INTERVAL_BOUNDS: Record = { [FunnelConversionWindowTimeUnit.Second]: [1, 3600], diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelation.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelation.tsx index 55718cd3ffc33..1933f49b96a03 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelation.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelation.tsx @@ -1,17 +1,17 @@ -import { useMountedLogic, useValues } from 'kea' +import './FunnelCorrelation.scss' -import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { useMountedLogic, useValues } from 'kea' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' import { funnelCorrelationUsageLogic } from 'scenes/funnels/funnelCorrelationUsageLogic' +import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { AvailableFeature } from '~/types' + +import { FunnelCorrelationFeedbackForm } from './FunnelCorrelationFeedbackForm' import { FunnelCorrelationSkewWarning } from './FunnelCorrelationSkewWarning' import { FunnelCorrelationTable } from './FunnelCorrelationTable' -import { FunnelCorrelationFeedbackForm } from './FunnelCorrelationFeedbackForm' import { FunnelPropertyCorrelationTable } from './FunnelPropertyCorrelationTable' -import { AvailableFeature } from '~/types' - -import './FunnelCorrelation.scss' export const FunnelCorrelation = (): JSX.Element | null => { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationFeedbackForm.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationFeedbackForm.tsx index d2838054e3e80..7399a0e979fc3 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationFeedbackForm.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationFeedbackForm.tsx @@ -1,11 +1,9 @@ -import { useRef } from 'react' -import { useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' -import { funnelCorrelationFeedbackLogic } from 'scenes/funnels/funnelCorrelationFeedbackLogic' - import { LemonButton, LemonTextArea } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { IconClose, IconComment } from 'lib/lemon-ui/icons' +import { useRef } from 'react' +import { funnelCorrelationFeedbackLogic } from 'scenes/funnels/funnelCorrelationFeedbackLogic' +import { insightLogic } from 'scenes/insights/insightLogic' export const FunnelCorrelationFeedbackForm = (): JSX.Element | null => { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationSkewWarning.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationSkewWarning.tsx index 587c33aefa508..6e4cc1416ffe0 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationSkewWarning.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationSkewWarning.tsx @@ -1,12 +1,10 @@ -import { useActions, useValues } from 'kea' -import { Card } from 'antd' - -import { insightLogic } from 'scenes/insights/insightLogic' - -import { IconFeedback } from 'lib/lemon-ui/icons' // eslint-disable-next-line no-restricted-imports import { CloseOutlined } from '@ant-design/icons' +import { Card } from 'antd' +import { useActions, useValues } from 'kea' +import { IconFeedback } from 'lib/lemon-ui/icons' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' export const FunnelCorrelationSkewWarning = (): JSX.Element | null => { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationTable.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationTable.tsx index 1c3a022249323..0501b62fe4b70 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationTable.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelCorrelationTable.tsx @@ -1,29 +1,31 @@ -import { useEffect } from 'react' -import { ConfigProvider, Table, Empty } from 'antd' +import './FunnelCorrelationTable.scss' + +import { IconInfo } from '@posthog/icons' +import { LemonCheckbox } from '@posthog/lemon-ui' +import { ConfigProvider, Empty, Table } from 'antd' import Column from 'antd/lib/table/Column' import { useActions, useValues } from 'kea' -import { IconSelectEvents, IconTrendUp, IconTrendingDown, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' -import { FunnelCorrelation, FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import './FunnelCorrelationTable.scss' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { VisibilitySensor } from 'lib/components/VisibilitySensor/VisibilitySensor' +import { IconSelectEvents, IconTrendingDown, IconTrendUp, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { CorrelationMatrix } from './CorrelationMatrix' -import { capitalizeFirstLetter } from 'lib/utils' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { EventCorrelationActionsCell } from './CorrelationActionsCell' import { Link } from 'lib/lemon-ui/Link' -import { funnelCorrelationUsageLogic } from 'scenes/funnels/funnelCorrelationUsageLogic' - +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter } from 'lib/utils' +import { useEffect } from 'react' import { funnelCorrelationLogic } from 'scenes/funnels/funnelCorrelationLogic' +import { funnelCorrelationUsageLogic } from 'scenes/funnels/funnelCorrelationUsageLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' -import { LemonCheckbox } from '@posthog/lemon-ui' import { funnelPersonsModalLogic } from 'scenes/funnels/funnelPersonsModalLogic' -import { IconInfo } from '@posthog/icons' +import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' +import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' +import { insightLogic } from 'scenes/insights/insightLogic' + +import { FunnelCorrelation, FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' + +import { EventCorrelationActionsCell } from './CorrelationActionsCell' +import { CorrelationMatrix } from './CorrelationMatrix' export function FunnelCorrelationTable(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelDisplayLayoutPicker.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelDisplayLayoutPicker.tsx index 3b4c95d14d886..1a21527301fad 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelDisplayLayoutPicker.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelDisplayLayoutPicker.tsx @@ -1,9 +1,9 @@ +import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { FunnelLayout } from 'lib/constants' -import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonSelect } from '@posthog/lemon-ui' import { IconFunnelHorizontal, IconFunnelVertical } from 'lib/lemon-ui/icons' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' export function FunnelDisplayLayoutPicker(): JSX.Element { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx index 3325b103ba438..fbede9951a858 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelPropertyCorrelationTable.tsx @@ -1,28 +1,31 @@ -import { useEffect } from 'react' -import { Link } from 'lib/lemon-ui/Link' -import { Col, ConfigProvider, Row, Table, Empty } from 'antd' +import './FunnelCorrelationTable.scss' + +import { IconInfo } from '@posthog/icons' +import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' +import { Col, ConfigProvider, Empty, Row, Table } from 'antd' import Column from 'antd/lib/table/Column' import { useActions, useValues } from 'kea' -import { FunnelCorrelation, FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' +import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { IconSelectProperties, IconTrendingDown, IconTrendingUp } from 'lib/lemon-ui/icons' -import './FunnelCorrelationTable.scss' import { VisibilitySensor } from 'lib/components/VisibilitySensor/VisibilitySensor' +import { IconSelectProperties, IconTrendingDown, IconTrendingUp } from 'lib/lemon-ui/icons' +import { Link } from 'lib/lemon-ui/Link' +import { Popover } from 'lib/lemon-ui/Popover' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { capitalizeFirstLetter } from 'lib/utils' -import { PropertyCorrelationActionsCell } from './CorrelationActionsCell' +import { useEffect } from 'react' +import { useState } from 'react' import { funnelCorrelationUsageLogic } from 'scenes/funnels/funnelCorrelationUsageLogic' -import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' -import { funnelPropertyCorrelationLogic } from 'scenes/funnels/funnelPropertyCorrelationLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { Popover } from 'lib/lemon-ui/Popover' -import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' -import { useState } from 'react' -import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' import { funnelPersonsModalLogic } from 'scenes/funnels/funnelPersonsModalLogic' -import { IconInfo } from '@posthog/icons' +import { funnelPropertyCorrelationLogic } from 'scenes/funnels/funnelPropertyCorrelationLogic' +import { parseDisplayNameForCorrelation } from 'scenes/funnels/funnelUtils' +import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' +import { insightLogic } from 'scenes/insights/insightLogic' + +import { FunnelCorrelation, FunnelCorrelationResultsType, FunnelCorrelationType } from '~/types' + +import { PropertyCorrelationActionsCell } from './CorrelationActionsCell' export function FunnelPropertyCorrelationTable(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelStepOrderPicker.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelStepOrderPicker.tsx index 54bc248662a91..615fc8bfa1a77 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelStepOrderPicker.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelStepOrderPicker.tsx @@ -1,11 +1,10 @@ +import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' - -import { insightLogic } from 'scenes/insights/insightLogic' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' +import { insightLogic } from 'scenes/insights/insightLogic' -import { StepOrderValue } from '~/types' -import { LemonSelect } from '@posthog/lemon-ui' import { FunnelsFilter } from '~/queries/schema' +import { StepOrderValue } from '~/types' interface StepOption { key?: string diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelStepsPicker.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelStepsPicker.tsx index ba9f0b36b07a4..c875b1cec5355 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelStepsPicker.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelStepsPicker.tsx @@ -1,11 +1,11 @@ +import { LemonSelect, LemonSelectOption, LemonSelectOptions } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' - import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonSelect, LemonSelectOptions, LemonSelectOption } from '@posthog/lemon-ui' -import { seriesNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { seriesNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' + export function FunnelStepsPicker(): JSX.Element | null { const { insightProps } = useValues(insightLogic) const { series, isFunnelWithEnoughSteps, funnelsFilter } = useValues(insightVizDataLogic(insightProps)) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelStepsTable.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelStepsTable.tsx index 0d2c3471ba6ee..ab8bae047fc37 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelStepsTable.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelStepsTable.tsx @@ -1,23 +1,25 @@ import { useActions, useValues } from 'kea' -import { insightLogic } from 'scenes/insights/insightLogic' -import { LemonTable, LemonTableColumn, LemonTableColumnGroup } from 'lib/lemon-ui/LemonTable' -import { FlattenedFunnelStepByBreakdown } from '~/types' +import { getSeriesColor } from 'lib/colors' import { EntityFilterInfo } from 'lib/components/EntityFilterInfo' -import { getVisibilityKey } from 'scenes/funnels/funnelUtils' -import { getActionFilterFromFunnelStep, getSignificanceFromBreakdownStep } from './funnelStepTableUtils' -import { cohortsModel } from '~/models/cohortsModel' +import { IconFlag } from 'lib/lemon-ui/icons' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' import { LemonRow } from 'lib/lemon-ui/LemonRow' +import { LemonTable, LemonTableColumn, LemonTableColumnGroup } from 'lib/lemon-ui/LemonTable' +import { Lettermark, LettermarkColor } from 'lib/lemon-ui/Lettermark' import { humanFriendlyDuration, humanFriendlyNumber, percentage } from 'lib/utils' -import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' -import { getSeriesColor } from 'lib/colors' -import { IconFlag } from 'lib/lemon-ui/icons' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatBreakdownLabel } from 'scenes/insights/utils' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { funnelPersonsModalLogic } from 'scenes/funnels/funnelPersonsModalLogic' +import { getVisibilityKey } from 'scenes/funnels/funnelUtils' +import { ValueInspectorButton } from 'scenes/funnels/ValueInspectorButton' +import { insightLogic } from 'scenes/insights/insightLogic' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { formatBreakdownLabel } from 'scenes/insights/utils' + +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { FlattenedFunnelStepByBreakdown } from '~/types' + +import { getActionFilterFromFunnelStep, getSignificanceFromBreakdownStep } from './funnelStepTableUtils' export function FunnelStepsTable(): JSX.Element | null { const { insightProps, insightLoading } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Funnels/FunnelVizType.tsx b/frontend/src/scenes/insights/views/Funnels/FunnelVizType.tsx index 3f613df7d8a10..9850c935468c6 100644 --- a/frontend/src/scenes/insights/views/Funnels/FunnelVizType.tsx +++ b/frontend/src/scenes/insights/views/Funnels/FunnelVizType.tsx @@ -1,12 +1,10 @@ +import { IconClock, IconFilter, IconTrending } from '@posthog/icons' import { useActions, useValues } from 'kea' - +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' -import { FunnelVizType as VizType, EditorFilterProps } from '~/types' import { FunnelsFilter } from '~/queries/schema' -import { IconFilter, IconClock, IconTrending } from '@posthog/icons' - -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { EditorFilterProps, FunnelVizType as VizType } from '~/types' type LabelProps = { icon: JSX.Element diff --git a/frontend/src/scenes/insights/views/Histogram/Histogram.tsx b/frontend/src/scenes/insights/views/Histogram/Histogram.tsx index 11671e289ecae..52e807a65deb2 100644 --- a/frontend/src/scenes/insights/views/Histogram/Histogram.tsx +++ b/frontend/src/scenes/insights/views/Histogram/Histogram.tsx @@ -1,14 +1,15 @@ -import { useEffect } from 'react' +import './Histogram.scss' + import * as d3 from 'd3' -import { D3Selector, D3Transition, useD3 } from 'lib/hooks/useD3' +import { useActions, useValues } from 'kea' import { FunnelLayout } from 'lib/constants' -import { createRoundedRectPath, D3HistogramDatum, getConfig, INITIAL_CONFIG } from './histogramUtils' +import { D3Selector, D3Transition, useD3 } from 'lib/hooks/useD3' import { animate, getOrCreateEl, wrap } from 'lib/utils/d3Utils' - -import './Histogram.scss' -import { useActions, useValues } from 'kea' +import { useEffect } from 'react' import { histogramLogic } from 'scenes/insights/views/Histogram/histogramLogic' +import { createRoundedRectPath, D3HistogramDatum, getConfig, INITIAL_CONFIG } from './histogramUtils' + export interface HistogramDatum { id: string | number bin0: number diff --git a/frontend/src/scenes/insights/views/Histogram/histogramLogic.test.ts b/frontend/src/scenes/insights/views/Histogram/histogramLogic.test.ts index 5dee7f5acfce8..254d7ad4e5ddc 100644 --- a/frontend/src/scenes/insights/views/Histogram/histogramLogic.test.ts +++ b/frontend/src/scenes/insights/views/Histogram/histogramLogic.test.ts @@ -1,7 +1,9 @@ -import { histogramLogic } from './histogramLogic' -import { initKeaTests } from '~/test/init' -import { getConfig } from 'scenes/insights/views/Histogram/histogramUtils' import { FunnelLayout } from 'lib/constants' +import { getConfig } from 'scenes/insights/views/Histogram/histogramUtils' + +import { initKeaTests } from '~/test/init' + +import { histogramLogic } from './histogramLogic' describe('histogramLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/insights/views/Histogram/histogramLogic.ts b/frontend/src/scenes/insights/views/Histogram/histogramLogic.ts index a3bcd68aeaca9..2993434027fec 100644 --- a/frontend/src/scenes/insights/views/Histogram/histogramLogic.ts +++ b/frontend/src/scenes/insights/views/Histogram/histogramLogic.ts @@ -1,7 +1,8 @@ -import { kea, path, actions, reducers } from 'kea' +import { actions, kea, path, reducers } from 'kea' +import { FunnelLayout } from 'lib/constants' import { getConfig, HistogramConfig } from 'scenes/insights/views/Histogram/histogramUtils' + import type { histogramLogicType } from './histogramLogicType' -import { FunnelLayout } from 'lib/constants' export const histogramLogic = kea([ path(['scenes', 'insights', 'Histogram', 'histogramLogic']), diff --git a/frontend/src/scenes/insights/views/InsightsTable/DashboardInsightsTable.tsx b/frontend/src/scenes/insights/views/InsightsTable/DashboardInsightsTable.tsx index 6ed7c8f0c9df7..f01085a698529 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/DashboardInsightsTable.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/DashboardInsightsTable.tsx @@ -1,5 +1,4 @@ import { useValues } from 'kea' - import { insightLogic } from 'scenes/insights/insightLogic' import { InsightsTable } from './InsightsTable' diff --git a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.stories.tsx b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.stories.tsx index 5b3283417f019..0d658d852b08f 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.stories.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.stories.tsx @@ -1,16 +1,15 @@ -import { useState } from 'react' -import { BindLogic } from 'kea' import { Meta, StoryFn, StoryObj } from '@storybook/react' - +import { BindLogic } from 'kea' +import { useState } from 'react' import { insightLogic } from 'scenes/insights/insightLogic' -import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' + import { dataNodeLogic, DataNodeLogicProps } from '~/queries/nodes/DataNode/dataNodeLogic' import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode' - +import { insightVizDataNodeKey } from '~/queries/nodes/InsightViz/InsightViz' +import { getCachedResults } from '~/queries/nodes/InsightViz/utils' import { BaseMathType, InsightLogicProps } from '~/types' import { InsightsTable } from './InsightsTable' -import { getCachedResults } from '~/queries/nodes/InsightViz/utils' type Story = StoryObj const meta: Meta = { diff --git a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.tsx b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.tsx index 4af3720692485..004bf853b61ca 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/InsightsTable.tsx @@ -1,27 +1,29 @@ -import { useActions, useValues } from 'kea' -import { cohortsModel } from '~/models/cohortsModel' -import { ChartDisplayType, ItemMode } from '~/types' -import { CalcColumnState } from './insightsTableLogic' -import { IndexedTrendResult } from 'scenes/trends/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { entityFilterLogic } from '../../filters/ActionFilter/entityFilterLogic' import './InsightsTable.scss' + +import { useActions, useValues } from 'kea' +import { getSeriesColor } from 'lib/colors' import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { countryCodeToName } from '../WorldMap' -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatBreakdownLabel } from 'scenes/insights/utils' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' import { isTrendsFilter } from 'scenes/insights/sharedUtils' +import { formatBreakdownLabel } from 'scenes/insights/utils' +import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' +import { IndexedTrendResult } from 'scenes/trends/types' -import { SeriesCheckColumnTitle, SeriesCheckColumnItem } from './columns/SeriesCheckColumn' -import { SeriesColumnItem } from './columns/SeriesColumn' -import { BreakdownColumnTitle, BreakdownColumnItem } from './columns/BreakdownColumn' -import { WorldMapColumnTitle, WorldMapColumnItem } from './columns/WorldMapColumn' +import { cohortsModel } from '~/models/cohortsModel' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' +import { ChartDisplayType, ItemMode } from '~/types' + +import { entityFilterLogic } from '../../filters/ActionFilter/entityFilterLogic' +import { countryCodeToName } from '../WorldMap' import { AggregationColumnItem, AggregationColumnTitle } from './columns/AggregationColumn' +import { BreakdownColumnItem, BreakdownColumnTitle } from './columns/BreakdownColumn' +import { SeriesCheckColumnItem, SeriesCheckColumnTitle } from './columns/SeriesCheckColumn' +import { SeriesColumnItem } from './columns/SeriesColumn' import { ValueColumnItem, ValueColumnTitle } from './columns/ValueColumn' +import { WorldMapColumnItem, WorldMapColumnTitle } from './columns/WorldMapColumn' import { AggregationType, insightsTableDataLogic } from './insightsTableDataLogic' -import { trendsDataLogic } from 'scenes/trends/trendsDataLogic' -import { getSeriesColor } from 'lib/colors' +import { CalcColumnState } from './insightsTableLogic' export interface InsightsTableProps { /** Whether this is just a legend instead of standalone insight viz. Default: false. */ diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/AggregationColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/AggregationColumn.tsx index ba2ec9f539329..17c37aa67e073 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/AggregationColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/AggregationColumn.tsx @@ -1,19 +1,18 @@ -import { useValues, useActions } from 'kea' -import { Dropdown, Menu } from 'antd' // eslint-disable-next-line no-restricted-imports import { DownOutlined } from '@ant-design/icons' - -import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' - +import { Dropdown, Menu } from 'antd' +import { useActions, useValues } from 'kea' import { average, median } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { formatAggregationValue } from 'scenes/insights/utils' import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' +import { formatAggregationValue } from 'scenes/insights/utils' import { IndexedTrendResult } from 'scenes/trends/types' -import { CalcColumnState } from '../insightsTableLogic' -import { TrendsFilterType } from '~/types' +import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' import { TrendsFilter } from '~/queries/schema' +import { TrendsFilterType } from '~/types' + +import { CalcColumnState } from '../insightsTableLogic' const CALC_COLUMN_LABELS: Record = { total: 'Total Sum', diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/BreakdownColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/BreakdownColumn.tsx index b64d677c8f975..a81efe34981b7 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/BreakdownColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/BreakdownColumn.tsx @@ -1,8 +1,9 @@ -import { BreakdownFilter } from '~/queries/schema' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import stringWithWBR from 'lib/utils/stringWithWBR' -import { IndexedTrendResult } from 'scenes/trends/types' import { formatBreakdownType } from 'scenes/insights/utils' +import { IndexedTrendResult } from 'scenes/trends/types' + +import { BreakdownFilter } from '~/queries/schema' type BreakdownColumnTitleProps = { breakdownFilter: BreakdownFilter } diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx index 785744697466f..81896e494e251 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/SeriesColumn.tsx @@ -1,11 +1,12 @@ import clsx from 'clsx' import { getSeriesColor } from 'lib/colors' -import { IndexedTrendResult } from 'scenes/trends/types' import { InsightLabel } from 'lib/components/InsightLabel' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconEdit } from 'lib/lemon-ui/icons' -import { TrendResult } from '~/types' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { capitalizeFirstLetter } from 'lib/utils' +import { IndexedTrendResult } from 'scenes/trends/types' + +import { TrendResult } from '~/types' type SeriesColumnItemProps = { item: IndexedTrendResult diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/ValueColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/ValueColumn.tsx index 1273048ef0b1e..26b225cae477e 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/ValueColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/ValueColumn.tsx @@ -1,11 +1,12 @@ import { useValues } from 'kea' -import { IndexedTrendResult } from 'scenes/trends/types' import { DateDisplay } from 'lib/components/DateDisplay' -import { IntervalType, TrendsFilterType } from '~/types' -import { formatAggregationValue } from 'scenes/insights/utils' import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' +import { formatAggregationValue } from 'scenes/insights/utils' +import { IndexedTrendResult } from 'scenes/trends/types' + import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' import { TrendsFilter } from '~/queries/schema' +import { IntervalType, TrendsFilterType } from '~/types' type ValueColumnTitleProps = { index: number diff --git a/frontend/src/scenes/insights/views/InsightsTable/columns/WorldMapColumn.tsx b/frontend/src/scenes/insights/views/InsightsTable/columns/WorldMapColumn.tsx index 7efdf58de5844..a05e2d00717cf 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/columns/WorldMapColumn.tsx +++ b/frontend/src/scenes/insights/views/InsightsTable/columns/WorldMapColumn.tsx @@ -1,5 +1,6 @@ -import { IndexedTrendResult } from 'scenes/trends/types' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { IndexedTrendResult } from 'scenes/trends/types' + import { countryCodeToName } from '../../WorldMap' export function WorldMapColumnTitle(): JSX.Element { diff --git a/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.test.ts b/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.test.ts index 46357caafc616..30ddcd27e2a4a 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.test.ts +++ b/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.test.ts @@ -1,11 +1,11 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { BaseMathType, ChartDisplayType, InsightShortId, PropertyMathType } from '~/types' import { NodeKind, TrendsQuery } from '~/queries/schema' +import { initKeaTests } from '~/test/init' +import { BaseMathType, ChartDisplayType, InsightShortId, PropertyMathType } from '~/types' -import { insightsTableDataLogic, AggregationType } from './insightsTableDataLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { AggregationType, insightsTableDataLogic } from './insightsTableDataLogic' const Insight123 = '123' as InsightShortId diff --git a/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.ts b/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.ts index cb40c623122b6..1d9a2806444c9 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.ts +++ b/frontend/src/scenes/insights/views/InsightsTable/insightsTableDataLogic.ts @@ -1,11 +1,10 @@ -import { kea, props, key, path, connect, actions, reducers, selectors } from 'kea' +import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { ChartDisplayType, InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' - import type { insightsTableDataLogicType } from './insightsTableDataLogicType' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' export enum AggregationType { Total = 'total', diff --git a/frontend/src/scenes/insights/views/InsightsTable/insightsTableLogic.ts b/frontend/src/scenes/insights/views/InsightsTable/insightsTableLogic.ts index a4c1c8962a0f3..04d17c2c5a031 100644 --- a/frontend/src/scenes/insights/views/InsightsTable/insightsTableLogic.ts +++ b/frontend/src/scenes/insights/views/InsightsTable/insightsTableLogic.ts @@ -1,7 +1,9 @@ -import { kea, props, path, actions, reducers, selectors } from 'kea' +import { actions, kea, path, props, reducers, selectors } from 'kea' +import { isTrendsFilter } from 'scenes/insights/sharedUtils' + import { ChartDisplayType, FilterType } from '~/types' + import type { insightsTableLogicType } from './insightsTableLogicType' -import { isTrendsFilter } from 'scenes/insights/sharedUtils' export type CalcColumnState = 'total' | 'average' | 'median' diff --git a/frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx b/frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx index 99f5080c825db..88bc33ee22fea 100644 --- a/frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx +++ b/frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx @@ -1,5 +1,8 @@ -import { useEffect, useRef, useState } from 'react' -import { Root, createRoot } from 'react-dom/client' +import 'chartjs-adapter-dayjs-3' + +import ChartDataLabels from 'chartjs-plugin-datalabels' +import ChartjsPluginStacked100, { ExtendedChartData } from 'chartjs-plugin-stacked100' +import clsx from 'clsx' import { useValues } from 'kea' import { ActiveElement, @@ -10,34 +13,33 @@ import { ChartOptions, ChartType, Color, + GridLineOptions, InteractionItem, + ScriptableLineSegmentContext, TickOptions, - GridLineOptions, TooltipModel, TooltipOptions, - ScriptableLineSegmentContext, } from 'lib/Chart' -import ChartDataLabels from 'chartjs-plugin-datalabels' -import 'chartjs-adapter-dayjs-3' -import { areObjectValuesEmpty, lightenDarkenColor, hexToRGBA } from '~/lib/utils' import { getBarColorFromStatus, getGraphColors, getSeriesColor } from 'lib/colors' import { AnnotationsOverlay } from 'lib/components/AnnotationsOverlay' -import { GraphDataset, GraphPoint, GraphPointPayload, GraphType } from '~/types' -import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' -import { lineGraphLogic } from 'scenes/insights/views/LineGraph/lineGraphLogic' -import { TooltipConfig } from 'scenes/insights/InsightTooltip/insightTooltipUtils' -import { groupsModel } from '~/models/groupsModel' -import { ErrorBoundary } from '~/layout/ErrorBoundary' +import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { useResizeObserver } from 'lib/hooks/useResizeObserver' +import { useEffect, useRef, useState } from 'react' +import { createRoot, Root } from 'react-dom/client' import { formatAggregationAxisValue, formatPercentStackAxisValue } from 'scenes/insights/aggregationAxisFormat' import { insightLogic } from 'scenes/insights/insightLogic' -import { useResizeObserver } from 'lib/hooks/useResizeObserver' +import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' +import { TooltipConfig } from 'scenes/insights/InsightTooltip/insightTooltipUtils' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { lineGraphLogic } from 'scenes/insights/views/LineGraph/lineGraphLogic' import { PieChart } from 'scenes/insights/views/LineGraph/PieChart' + +import { ErrorBoundary } from '~/layout/ErrorBoundary' import { themeLogic } from '~/layout/navigation-3000/themeLogic' -import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { areObjectValuesEmpty, hexToRGBA, lightenDarkenColor } from '~/lib/utils' +import { groupsModel } from '~/models/groupsModel' import { TrendsFilter } from '~/queries/schema' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import ChartjsPluginStacked100, { ExtendedChartData } from 'chartjs-plugin-stacked100' -import clsx from 'clsx' +import { GraphDataset, GraphPoint, GraphPointPayload, GraphType } from '~/types' let tooltipRoot: Root diff --git a/frontend/src/scenes/insights/views/LineGraph/PieChart.tsx b/frontend/src/scenes/insights/views/LineGraph/PieChart.tsx index f58924882f82d..d3424bbcb8fc2 100644 --- a/frontend/src/scenes/insights/views/LineGraph/PieChart.tsx +++ b/frontend/src/scenes/insights/views/LineGraph/PieChart.tsx @@ -1,19 +1,24 @@ -import { useEffect, useRef } from 'react' +import 'chartjs-adapter-dayjs-3' + +import ChartDataLabels, { Context } from 'chartjs-plugin-datalabels' +import { useActions, useValues } from 'kea' import { ActiveElement, Chart, + ChartDataset, ChartEvent, ChartItem, - ChartType, - TooltipModel, ChartOptions, - ChartDataset, + ChartType, Plugin, + TooltipModel, } from 'lib/Chart' -import 'chartjs-adapter-dayjs-3' -import { areObjectValuesEmpty } from '~/lib/utils' -import { GraphType } from '~/types' +import { SeriesLetter } from 'lib/components/SeriesGlyph' +import { useEffect, useRef } from 'react' import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' +import { insightLogic } from 'scenes/insights/insightLogic' +import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' +import { SeriesDatum } from 'scenes/insights/InsightTooltip/insightTooltipUtils' import { ensureTooltip, filterNestedDataset, @@ -21,14 +26,11 @@ import { onChartClick, onChartHover, } from 'scenes/insights/views/LineGraph/LineGraph' -import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' -import { useActions, useValues } from 'kea' -import { groupsModel } from '~/models/groupsModel' import { lineGraphLogic } from 'scenes/insights/views/LineGraph/lineGraphLogic' -import { insightLogic } from 'scenes/insights/insightLogic' -import { SeriesDatum } from 'scenes/insights/InsightTooltip/insightTooltipUtils' -import { SeriesLetter } from 'lib/components/SeriesGlyph' -import ChartDataLabels, { Context } from 'chartjs-plugin-datalabels' + +import { areObjectValuesEmpty } from '~/lib/utils' +import { groupsModel } from '~/models/groupsModel' +import { GraphType } from '~/types' let timer: NodeJS.Timeout | null = null @@ -50,6 +52,11 @@ function getPercentageForDataPoint(context: Context): number { return ((context.dataset.data[context.dataIndex] as number) / total) * 100 } +export interface PieChartProps extends LineGraphProps { + showLabelOnSeries?: boolean | null + disableHoverOffset?: boolean | null +} + export function PieChart({ datasets: _datasets, hiddenLegendKeys, @@ -60,12 +67,14 @@ export function PieChart({ trendsFilter, formula, showValueOnSeries, + showLabelOnSeries, supportsPercentStackView, showPercentStackView, tooltip: tooltipConfig, showPersonsModal = true, labelGroupType, -}: LineGraphProps): JSX.Element { + disableHoverOffset, +}: PieChartProps): JSX.Element { const isPie = type === GraphType.Pie const isPercentStackView = !!supportsPercentStackView && !!showPercentStackView @@ -114,12 +123,14 @@ export function PieChart({ layout: { padding: { top: 12, // 12 px so that the label isn't cropped + left: 20, + right: 20, bottom: 20, // 12 px so that the label isn't cropped + 8 px of padding against the number below }, }, borderWidth: 0, borderRadius: 0, - hoverOffset: onlyOneValue ? 0 : 16, // don't offset hovered segment if it is 100% + hoverOffset: onlyOneValue || disableHoverOffset ? 0 : 16, // don't offset hovered segment if it is 100% onHover(event: ChartEvent, _: ActiveElement[], chart: Chart) { onChartHover(event, chart, onClick) }, @@ -135,7 +146,8 @@ export function PieChart({ }, display: (context) => { const percentage = getPercentageForDataPoint(context) - return showValueOnSeries !== false && // show if true or unset + return (showValueOnSeries !== false || // show if true or unset + showLabelOnSeries) && context.dataset.data.length > 1 && percentage > 5 ? 'auto' @@ -149,6 +161,10 @@ export function PieChart({ return { top: paddingY, bottom: paddingY, left: paddingX, right: paddingX } }, formatter: (value: number, context) => { + if (showLabelOnSeries) { + // cast to any as it seems like TypeScript types are wrong + return (context.dataset as any).labels?.[context.dataIndex] + } if (isPercentStackView) { const percentage = getPercentageForDataPoint(context) return `${percentage.toFixed(1)}%` diff --git a/frontend/src/scenes/insights/views/LineGraph/lineGraphLogic.ts b/frontend/src/scenes/insights/views/LineGraph/lineGraphLogic.ts index e7473601a28b7..a5df4b88f6c79 100644 --- a/frontend/src/scenes/insights/views/LineGraph/lineGraphLogic.ts +++ b/frontend/src/scenes/insights/views/LineGraph/lineGraphLogic.ts @@ -1,7 +1,9 @@ -import { kea, path, selectors } from 'kea' import { TooltipItem } from 'chart.js' -import { GraphDataset } from '~/types' +import { kea, path, selectors } from 'kea' import { SeriesDatum } from 'scenes/insights/InsightTooltip/insightTooltipUtils' + +import { GraphDataset } from '~/types' + import type { lineGraphLogicType } from './lineGraphLogicType' // TODO: Eventually we should move all state from LineGraph into this logic diff --git a/frontend/src/scenes/insights/views/Paths/PathStepPicker.tsx b/frontend/src/scenes/insights/views/Paths/PathStepPicker.tsx index 74fcf78ea4515..a618625785b41 100644 --- a/frontend/src/scenes/insights/views/Paths/PathStepPicker.tsx +++ b/frontend/src/scenes/insights/views/Paths/PathStepPicker.tsx @@ -1,15 +1,14 @@ -import { useActions, useValues } from 'kea' -import { Select } from 'antd' // eslint-disable-next-line no-restricted-imports import { BarsOutlined } from '@ant-design/icons' +import { Select } from 'antd' +import { useActions, useValues } from 'kea' import { ANTD_TOOLTIP_PLACEMENTS } from 'lib/utils' - +import { insightLogic } from 'scenes/insights/insightLogic' import { DEFAULT_STEP_LIMIT } from 'scenes/paths/pathsDataLogic' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' import { userLogic } from 'scenes/userLogic' import { AvailableFeature } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' interface StepOption { label: string diff --git a/frontend/src/scenes/insights/views/Trends/FunnelsCue.tsx b/frontend/src/scenes/insights/views/Trends/FunnelsCue.tsx index 263a633226135..c7f9433a99bf6 100644 --- a/frontend/src/scenes/insights/views/Trends/FunnelsCue.tsx +++ b/frontend/src/scenes/insights/views/Trends/FunnelsCue.tsx @@ -1,7 +1,7 @@ import { useActions, useValues } from 'kea' -import { funnelsCueLogic } from 'scenes/insights/views/Trends/funnelsCueLogic' -import { insightLogic } from 'scenes/insights/insightLogic' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { insightLogic } from 'scenes/insights/insightLogic' +import { funnelsCueLogic } from 'scenes/insights/views/Trends/funnelsCueLogic' export function FunnelsCue(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/insights/views/Trends/funnelsCueLogic.tsx b/frontend/src/scenes/insights/views/Trends/funnelsCueLogic.tsx index 4fb90ffd57480..fce1c5c8c6f04 100644 --- a/frontend/src/scenes/insights/views/Trends/funnelsCueLogic.tsx +++ b/frontend/src/scenes/insights/views/Trends/funnelsCueLogic.tsx @@ -1,14 +1,16 @@ -import { kea, props, key, path, connect, actions, reducers, listeners, selectors, events } from 'kea' -import { InsightLogicProps } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { insightLogic } from 'scenes/insights/insightLogic' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import posthog from 'posthog-js' -import { FEATURE_FLAGS } from 'lib/constants' -import type { funnelsCueLogicType } from './funnelsCueLogicType' +import { insightLogic } from 'scenes/insights/insightLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { isFunnelsQuery, isInsightVizNode, isTrendsQuery } from '~/queries/utils' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + import { InsightVizNode, NodeKind } from '~/queries/schema' +import { isFunnelsQuery, isInsightVizNode, isTrendsQuery } from '~/queries/utils' +import { InsightLogicProps } from '~/types' + +import type { funnelsCueLogicType } from './funnelsCueLogicType' export const funnelsCueLogic = kea([ props({} as InsightLogicProps), diff --git a/frontend/src/scenes/insights/views/WorldMap/WorldMap.tsx b/frontend/src/scenes/insights/views/WorldMap/WorldMap.tsx index 472ad00a1aaeb..5085cfd419acd 100644 --- a/frontend/src/scenes/insights/views/WorldMap/WorldMap.tsx +++ b/frontend/src/scenes/insights/views/WorldMap/WorldMap.tsx @@ -1,18 +1,21 @@ -import { useValues, useActions } from 'kea' +import './WorldMap.scss' + +import { useActions, useValues } from 'kea' +import { BRAND_BLUE_HSL, gradateColor } from 'lib/colors' import React, { HTMLProps, useEffect, useRef } from 'react' +import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' import { insightLogic } from 'scenes/insights/insightLogic' -import { ChartDisplayType, ChartParams, TrendResult } from '~/types' -import './WorldMap.scss' import { InsightTooltip } from 'scenes/insights/InsightTooltip/InsightTooltip' +import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' + +import { groupsModel } from '~/models/groupsModel' +import { ChartDisplayType, ChartParams, TrendResult } from '~/types' + import { SeriesDatum } from '../../InsightTooltip/insightTooltipUtils' import { ensureTooltip } from '../LineGraph/LineGraph' -import { worldMapLogic } from './worldMapLogic' import { countryCodeToFlag, countryCodeToName } from './countryCodes' import { countryVectors } from './countryVectors' -import { groupsModel } from '~/models/groupsModel' -import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' -import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' -import { BRAND_BLUE_HSL, gradateColor } from 'lib/colors' +import { worldMapLogic } from './worldMapLogic' /** The saturation of a country is proportional to its value BUT the saturation has a floor to improve visibility. */ const SATURATION_FLOOR = 0.2 diff --git a/frontend/src/scenes/insights/views/WorldMap/index.ts b/frontend/src/scenes/insights/views/WorldMap/index.ts index 3b4866cf21dca..ad0fc13da1f54 100644 --- a/frontend/src/scenes/insights/views/WorldMap/index.ts +++ b/frontend/src/scenes/insights/views/WorldMap/index.ts @@ -1,2 +1,2 @@ -export { WorldMap } from './WorldMap' export { countryCodeToFlag, countryCodeToName } from './countryCodes' +export { WorldMap } from './WorldMap' diff --git a/frontend/src/scenes/insights/views/WorldMap/worldMapLogic.tsx b/frontend/src/scenes/insights/views/WorldMap/worldMapLogic.tsx index 755c7f1e22445..534e66560ca4b 100644 --- a/frontend/src/scenes/insights/views/WorldMap/worldMapLogic.tsx +++ b/frontend/src/scenes/insights/views/WorldMap/worldMapLogic.tsx @@ -1,6 +1,8 @@ -import { kea, props, key, path, connect, actions, reducers, selectors } from 'kea' +import { actions, connect, kea, key, path, props, reducers, selectors } from 'kea' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' + import { InsightLogicProps, TrendResult } from '~/types' + import { keyForInsightLogicProps } from '../../sharedUtils' import type { worldMapLogicType } from './worldMapLogicType' diff --git a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationDetails.tsx b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationDetails.tsx index 1d14c6223dea7..b0e523bef5788 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationDetails.tsx +++ b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationDetails.tsx @@ -1,10 +1,11 @@ -import { AsyncMigration, AsyncMigrationError, asyncMigrationsLogic } from './asyncMigrationsLogic' -import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { useActions, useValues } from 'kea' +import { IconRefresh } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { humanFriendlyDetailedTime } from 'lib/utils' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconRefresh } from 'lib/lemon-ui/icons' + +import { AsyncMigration, AsyncMigrationError, asyncMigrationsLogic } from './asyncMigrationsLogic' export function AsyncMigrationDetails({ asyncMigration }: { asyncMigration: AsyncMigration }): JSX.Element { const { asyncMigrationErrorsLoading, asyncMigrationErrors } = useValues(asyncMigrationsLogic) diff --git a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationParametersModal.tsx b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationParametersModal.tsx index a0deb2a283041..16dbf65a8ce9d 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationParametersModal.tsx +++ b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrationParametersModal.tsx @@ -1,13 +1,13 @@ -import { useState } from 'react' +import { Link } from '@posthog/lemon-ui' import { useActions } from 'kea' -import { AsyncMigrationModalProps, asyncMigrationsLogic } from 'scenes/instance/AsyncMigrations/asyncMigrationsLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { asyncMigrationParameterFormLogic } from 'scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic' import { Field, Form } from 'kea-forms' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { Link } from '@posthog/lemon-ui' +import { useState } from 'react' +import { asyncMigrationParameterFormLogic } from 'scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic' +import { AsyncMigrationModalProps, asyncMigrationsLogic } from 'scenes/instance/AsyncMigrations/asyncMigrationsLogic' export function AsyncMigrationParametersModal(props: AsyncMigrationModalProps): JSX.Element { const { closeAsyncMigrationsModal } = useActions(asyncMigrationsLogic) diff --git a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrations.tsx b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrations.tsx index dc1be09924ebc..4bc890c80f38a 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrations.tsx +++ b/frontend/src/scenes/instance/AsyncMigrations/AsyncMigrations.tsx @@ -1,29 +1,30 @@ -import { useEffect } from 'react' -import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' +import { Link } from '@posthog/lemon-ui' import { Button, Progress } from 'antd' import { useActions, useValues } from 'kea' +import { PageHeader } from 'lib/components/PageHeader' +import { IconPlayCircle, IconRefresh, IconReplay } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { LemonTag, LemonTagType } from 'lib/lemon-ui/LemonTag/LemonTag' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { humanFriendlyDetailedTime } from 'lib/utils' +import { useEffect } from 'react' +import { AsyncMigrationParametersModal } from 'scenes/instance/AsyncMigrations/AsyncMigrationParametersModal' +import { SceneExport } from 'scenes/sceneTypes' +import { userLogic } from 'scenes/userLogic' + +import { AsyncMigrationDetails } from './AsyncMigrationDetails' import { AsyncMigration, - migrationStatusNumberToMessage, asyncMigrationsLogic, AsyncMigrationsTab, AsyncMigrationStatus, + migrationStatusNumberToMessage, } from './asyncMigrationsLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { userLogic } from 'scenes/userLogic' import { SettingUpdateField } from './SettingUpdateField' -import { LemonTable, LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { AsyncMigrationDetails } from './AsyncMigrationDetails' -import { humanFriendlyDetailedTime } from 'lib/utils' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonTag, LemonTagType } from 'lib/lemon-ui/LemonTag/LemonTag' -import { IconPlayCircle, IconRefresh, IconReplay } from 'lib/lemon-ui/icons' -import { AsyncMigrationParametersModal } from 'scenes/instance/AsyncMigrations/AsyncMigrationParametersModal' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { Link } from '@posthog/lemon-ui' export const scene: SceneExport = { component: AsyncMigrations, diff --git a/frontend/src/scenes/instance/AsyncMigrations/SettingUpdateField.tsx b/frontend/src/scenes/instance/AsyncMigrations/SettingUpdateField.tsx index 20bd8b9065626..415d832354f3e 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/SettingUpdateField.tsx +++ b/frontend/src/scenes/instance/AsyncMigrations/SettingUpdateField.tsx @@ -1,9 +1,11 @@ -import { useState } from 'react' import { Button, Col, Divider, Input, Row } from 'antd' import { useActions } from 'kea' -import { asyncMigrationsLogic } from './asyncMigrationsLogic' +import { useState } from 'react' + import { InstanceSetting } from '~/types' +import { asyncMigrationsLogic } from './asyncMigrationsLogic' + export function SettingUpdateField({ setting }: { setting: InstanceSetting }): JSX.Element { const { updateSetting } = useActions(asyncMigrationsLogic) diff --git a/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic.ts b/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic.ts index 449af49a92dfc..8cee7669a49f5 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic.ts +++ b/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationParameterFormLogic.ts @@ -1,4 +1,4 @@ -import { kea, key, props, path } from 'kea' +import { kea, key, path, props } from 'kea' import { forms } from 'kea-forms' import { AsyncMigrationModalProps, asyncMigrationsLogic } from 'scenes/instance/AsyncMigrations/asyncMigrationsLogic' diff --git a/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationsLogic.ts b/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationsLogic.ts index b87588dff5198..9966096c8a6d0 100644 --- a/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationsLogic.ts +++ b/frontend/src/scenes/instance/AsyncMigrations/asyncMigrationsLogic.ts @@ -1,14 +1,15 @@ +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { actionToUrl, urlToAction } from 'kea-router' import api from 'lib/api' -import { kea, path, connect, actions, events, listeners, selectors, reducers } from 'kea' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { systemStatusLogic } from 'scenes/instance/SystemStatus/systemStatusLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { userLogic } from 'scenes/userLogic' -import type { asyncMigrationsLogicType } from './asyncMigrationsLogicType' -import { systemStatusLogic } from 'scenes/instance/SystemStatus/systemStatusLogic' import { InstanceSetting } from '~/types' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { loaders } from 'kea-loaders' -import { actionToUrl, urlToAction } from 'kea-router' + +import type { asyncMigrationsLogicType } from './asyncMigrationsLogicType' export type TabName = 'overview' | 'internal_metrics' // keep in sync with MigrationStatus in posthog/models/async_migration.py diff --git a/frontend/src/scenes/instance/DeadLetterQueue/DeadLetterQueue.tsx b/frontend/src/scenes/instance/DeadLetterQueue/DeadLetterQueue.tsx index fa516dea91558..6b8a6933c39f6 100644 --- a/frontend/src/scenes/instance/DeadLetterQueue/DeadLetterQueue.tsx +++ b/frontend/src/scenes/instance/DeadLetterQueue/DeadLetterQueue.tsx @@ -1,10 +1,11 @@ +import { useActions, useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' import { SceneExport } from 'scenes/sceneTypes' -import { useValues, useActions } from 'kea' -import { deadLetterQueueLogic, DeadLetterQueueTab } from './deadLetterQueueLogic' import { userLogic } from 'scenes/userLogic' + +import { deadLetterQueueLogic, DeadLetterQueueTab } from './deadLetterQueueLogic' import { MetricsTab } from './MetricsTab' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' export const scene: SceneExport = { component: DeadLetterQueue, diff --git a/frontend/src/scenes/instance/DeadLetterQueue/MetricsTab.tsx b/frontend/src/scenes/instance/DeadLetterQueue/MetricsTab.tsx index 901500d92a6c4..430c80e85cdbf 100644 --- a/frontend/src/scenes/instance/DeadLetterQueue/MetricsTab.tsx +++ b/frontend/src/scenes/instance/DeadLetterQueue/MetricsTab.tsx @@ -1,11 +1,12 @@ import { Button, Col, Divider, Row, Statistic } from 'antd' -import { useValues, useActions } from 'kea' -import { deadLetterQueueLogic } from './deadLetterQueueLogic' -import { userLogic } from 'scenes/userLogic' -import { LemonTable } from 'lib/lemon-ui/LemonTable' +import { useActions, useValues } from 'kea' +import { IconRefresh } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonTable } from 'lib/lemon-ui/LemonTable' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { IconRefresh } from 'lib/lemon-ui/icons' +import { userLogic } from 'scenes/userLogic' + +import { deadLetterQueueLogic } from './deadLetterQueueLogic' // keep in sync with posthog/api/dead_letter_queue.py const ROWS_LIMIT = 10 diff --git a/frontend/src/scenes/instance/DeadLetterQueue/deadLetterQueueLogic.ts b/frontend/src/scenes/instance/DeadLetterQueue/deadLetterQueueLogic.ts index 4bd84df2e226d..2135874059cdc 100644 --- a/frontend/src/scenes/instance/DeadLetterQueue/deadLetterQueueLogic.ts +++ b/frontend/src/scenes/instance/DeadLetterQueue/deadLetterQueueLogic.ts @@ -1,10 +1,10 @@ -import { SystemStatusRow } from './../../../types' -import api from 'lib/api' import { actions, afterMount, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import api from 'lib/api' import { userLogic } from 'scenes/userLogic' +import { SystemStatusRow } from './../../../types' import type { deadLetterQueueLogicType } from './deadLetterQueueLogicType' -import { loaders } from 'kea-loaders' export type TabName = 'overview' | 'internal_metrics' export enum DeadLetterQueueTab { diff --git a/frontend/src/scenes/instance/SystemStatus/InstanceConfigSaveModal.tsx b/frontend/src/scenes/instance/SystemStatus/InstanceConfigSaveModal.tsx index 4bb150b48d68e..79d221898a27a 100644 --- a/frontend/src/scenes/instance/SystemStatus/InstanceConfigSaveModal.tsx +++ b/frontend/src/scenes/instance/SystemStatus/InstanceConfigSaveModal.tsx @@ -2,7 +2,9 @@ import { LemonButton, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { pluralize } from 'lib/utils' + import { SystemStatusRow } from '~/types' + import { RenderMetricValue } from './RenderMetricValue' import { systemStatusLogic } from './systemStatusLogic' diff --git a/frontend/src/scenes/instance/SystemStatus/InstanceConfigTab.tsx b/frontend/src/scenes/instance/SystemStatus/InstanceConfigTab.tsx index 6924ec179ffad..9d9f0eff4a507 100644 --- a/frontend/src/scenes/instance/SystemStatus/InstanceConfigTab.tsx +++ b/frontend/src/scenes/instance/SystemStatus/InstanceConfigTab.tsx @@ -1,16 +1,18 @@ +import { LemonButton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' import { IconOpenInNew, IconWarning } from 'lib/lemon-ui/icons' import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' +import { pluralize } from 'lib/utils' +import { useEffect } from 'react' import { EnvironmentConfigOption, preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { InstanceSetting } from '~/types' + +import { InstanceConfigSaveModal } from './InstanceConfigSaveModal' import { MetricValue, RenderMetricValue } from './RenderMetricValue' import { RenderMetricValueEdit } from './RenderMetricValueEdit' import { ConfigMode, systemStatusLogic } from './systemStatusLogic' -import { InstanceConfigSaveModal } from './InstanceConfigSaveModal' -import { pluralize } from 'lib/utils' -import { LemonButton, Link } from '@posthog/lemon-ui' -import { useEffect } from 'react' export function InstanceConfigTab(): JSX.Element { const { configOptions, preflightLoading } = useValues(preflightLogic) diff --git a/frontend/src/scenes/instance/SystemStatus/InternalMetricsTab.tsx b/frontend/src/scenes/instance/SystemStatus/InternalMetricsTab.tsx index a53f8afe10a5e..7451de6a05d29 100644 --- a/frontend/src/scenes/instance/SystemStatus/InternalMetricsTab.tsx +++ b/frontend/src/scenes/instance/SystemStatus/InternalMetricsTab.tsx @@ -1,12 +1,13 @@ -import { useMemo, useState } from 'react' +import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' import { Table } from 'antd' +import { ColumnsType } from 'antd/lib/table' import { useActions, useValues } from 'kea' +import { IconRefresh } from 'lib/lemon-ui/icons' +import { LemonCollapse } from 'lib/lemon-ui/LemonCollapse' +import { useMemo, useState } from 'react' import { systemStatusLogic } from 'scenes/instance/SystemStatus/systemStatusLogic' + import { QuerySummary } from '~/types' -import { ColumnsType } from 'antd/lib/table' -import { LemonCollapse } from 'lib/lemon-ui/LemonCollapse' -import { LemonButton, LemonCheckbox } from '@posthog/lemon-ui' -import { IconRefresh } from 'lib/lemon-ui/icons' export function InternalMetricsTab(): JSX.Element { const { openSections, queries, queriesLoading } = useValues(systemStatusLogic) diff --git a/frontend/src/scenes/instance/SystemStatus/KafkaInspectorTab.tsx b/frontend/src/scenes/instance/SystemStatus/KafkaInspectorTab.tsx index a61d9eb7e20cb..cbf13c19dcf59 100644 --- a/frontend/src/scenes/instance/SystemStatus/KafkaInspectorTab.tsx +++ b/frontend/src/scenes/instance/SystemStatus/KafkaInspectorTab.tsx @@ -1,10 +1,11 @@ -import { Button, Col, Divider, Row } from 'antd' +import { LemonButton, LemonDivider } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { kafkaInspectorLogic } from './kafkaInspectorLogic' import { Field, Form } from 'kea-forms' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { kafkaInspectorLogic } from './kafkaInspectorLogic' + export function KafkaInspectorTab(): JSX.Element { const { kafkaMessage } = useValues(kafkaInspectorLogic) @@ -12,7 +13,7 @@ export function KafkaInspectorTab(): JSX.Element {

    Kafka Inspector

    Debug Kafka messages using the inspector tool.
    - +
    - - +
    +
    - + - - +
    +
    - - {' '} - - + + +
    +
    - - {' '} - - - - - - + + +
    +
    + + Fetch Message + +
    +
    diff --git a/frontend/src/scenes/instance/SystemStatus/OverviewTab.tsx b/frontend/src/scenes/instance/SystemStatus/OverviewTab.tsx index ab3984d675038..71ed29426d9ab 100644 --- a/frontend/src/scenes/instance/SystemStatus/OverviewTab.tsx +++ b/frontend/src/scenes/instance/SystemStatus/OverviewTab.tsx @@ -1,10 +1,12 @@ -import { systemStatusLogic } from './systemStatusLogic' +import { LemonTable } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { SystemStatusRow, SystemStatusSubrows } from '~/types' import { IconOpenInNew } from 'lib/lemon-ui/icons' import { Link } from 'lib/lemon-ui/Link' + +import { SystemStatusRow, SystemStatusSubrows } from '~/types' + import { RenderMetricValue } from './RenderMetricValue' -import { LemonTable } from '@posthog/lemon-ui' +import { systemStatusLogic } from './systemStatusLogic' const METRIC_KEY_TO_INTERNAL_LINK = { async_migrations_ok: '/instance/async_migrations', diff --git a/frontend/src/scenes/instance/SystemStatus/RenderMetricValue.tsx b/frontend/src/scenes/instance/SystemStatus/RenderMetricValue.tsx index 89b3660a8d886..9d5b2a81083ab 100644 --- a/frontend/src/scenes/instance/SystemStatus/RenderMetricValue.tsx +++ b/frontend/src/scenes/instance/SystemStatus/RenderMetricValue.tsx @@ -1,7 +1,8 @@ +import { TZLabel } from '@posthog/apps-common' +import { IconLock } from 'lib/lemon-ui/icons' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' + import { InstanceSetting, SystemStatusRow } from '~/types' -import { IconLock } from 'lib/lemon-ui/icons' -import { TZLabel } from '@posthog/apps-common' const TIMESTAMP_VALUES = new Set(['last_event_ingested_timestamp']) diff --git a/frontend/src/scenes/instance/SystemStatus/RenderMetricValueEdit.tsx b/frontend/src/scenes/instance/SystemStatus/RenderMetricValueEdit.tsx index 20922721843a3..5f8f3acc9907c 100644 --- a/frontend/src/scenes/instance/SystemStatus/RenderMetricValueEdit.tsx +++ b/frontend/src/scenes/instance/SystemStatus/RenderMetricValueEdit.tsx @@ -1,5 +1,6 @@ import { LemonCheckbox, LemonInput } from '@posthog/lemon-ui' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' + import { MetricValue } from './RenderMetricValue' interface MetricValueEditInterface extends MetricValue { diff --git a/frontend/src/scenes/instance/SystemStatus/StaffUsersTab.tsx b/frontend/src/scenes/instance/SystemStatus/StaffUsersTab.tsx index 3605857912d5d..c9f0274320c1b 100644 --- a/frontend/src/scenes/instance/SystemStatus/StaffUsersTab.tsx +++ b/frontend/src/scenes/instance/SystemStatus/StaffUsersTab.tsx @@ -1,16 +1,18 @@ +import { Link } from '@posthog/lemon-ui' import { Divider, Modal } from 'antd' import { useActions, useValues } from 'kea' +import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' import { IconDelete } from 'lib/lemon-ui/icons' -import { LemonTableColumns, LemonTable } from 'lib/lemon-ui/LemonTable' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' +import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { userLogic } from 'scenes/userLogic' + import { UserType } from '~/types' + import { staffUsersLogic } from './staffUsersLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { userLogic } from 'scenes/userLogic' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' -import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' -import { Link } from '@posthog/lemon-ui' export function StaffUsersTab(): JSX.Element { const { user: myself } = useValues(userLogic) diff --git a/frontend/src/scenes/instance/SystemStatus/index.tsx b/frontend/src/scenes/instance/SystemStatus/index.tsx index 0c4e7d08eda42..69ccf9b44bc11 100644 --- a/frontend/src/scenes/instance/SystemStatus/index.tsx +++ b/frontend/src/scenes/instance/SystemStatus/index.tsx @@ -1,24 +1,24 @@ import './index.scss' -import { Alert } from 'antd' -import { systemStatusLogic, InstanceStatusTabName } from './systemStatusLogic' +import { LemonBanner, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { PageHeader } from 'lib/components/PageHeader' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { FEATURE_FLAGS } from 'lib/constants' import { IconInfo } from 'lib/lemon-ui/icons' -import { OverviewTab } from 'scenes/instance/SystemStatus/OverviewTab' +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 { InternalMetricsTab } from 'scenes/instance/SystemStatus/InternalMetricsTab' +import { OverviewTab } from 'scenes/instance/SystemStatus/OverviewTab' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { SceneExport } from 'scenes/sceneTypes' -import { InstanceConfigTab } from './InstanceConfigTab' import { userLogic } from 'scenes/userLogic' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { StaffUsersTab } from './StaffUsersTab' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' + +import { InstanceConfigTab } from './InstanceConfigTab' import { KafkaInspectorTab } from './KafkaInspectorTab' -import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Link } from '@posthog/lemon-ui' +import { StaffUsersTab } from './StaffUsersTab' +import { InstanceStatusTabName, systemStatusLogic } from './systemStatusLogic' export const scene: SceneExport = { component: SystemStatus, @@ -105,44 +105,35 @@ export function SystemStatus(): JSX.Element { } /> - {error && ( - An unknown error occurred. Please try again or contact us.} - type="error" - showIcon - /> - )} - {siteUrlMisconfigured && ( - - Your SITE_URL environment variable seems misconfigured. Your{' '} - SITE_URL is set to{' '} - - {preflight?.site_url} - {' '} - but you're currently browsing this page from{' '} - - {window.location.origin} - - . In order for PostHog to work properly, please set this to the origin where your instance - is hosted.{' '} - - Learn more - - - } - showIcon - type="warning" - style={{ marginBottom: 32 }} - /> - )} +
    + {error && ( + +
    Something went wrong
    +
    {error || 'An unknown error occurred. Please try again or contact us.'}
    +
    + )} + {siteUrlMisconfigured && ( + + Your SITE_URL environment variable seems misconfigured. Your SITE_URL{' '} + is set to{' '} + + {preflight?.site_url} + {' '} + but you're currently browsing this page from{' '} + + {window.location.origin} + + . In order for PostHog to work properly, please set this to the origin where your instance is + hosted. + + )} +
    diff --git a/frontend/src/scenes/instance/SystemStatus/kafkaInspectorLogic.ts b/frontend/src/scenes/instance/SystemStatus/kafkaInspectorLogic.ts index f10afcd1dd3e9..3fcb9422d906c 100644 --- a/frontend/src/scenes/instance/SystemStatus/kafkaInspectorLogic.ts +++ b/frontend/src/scenes/instance/SystemStatus/kafkaInspectorLogic.ts @@ -1,8 +1,9 @@ import { actions, kea, path } from 'kea' -import api from 'lib/api' -import type { kafkaInspectorLogicType } from './kafkaInspectorLogicType' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' +import api from 'lib/api' + +import type { kafkaInspectorLogicType } from './kafkaInspectorLogicType' export interface KafkaMessage { topic: string partition: number diff --git a/frontend/src/scenes/instance/SystemStatus/staffUsersLogic.ts b/frontend/src/scenes/instance/SystemStatus/staffUsersLogic.ts index 1f59748f42345..d246472c51228 100644 --- a/frontend/src/scenes/instance/SystemStatus/staffUsersLogic.ts +++ b/frontend/src/scenes/instance/SystemStatus/staffUsersLogic.ts @@ -1,10 +1,12 @@ +import { actions, connect, events, kea, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, events } from 'kea' import { router } from 'kea-router' import api from 'lib/api' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + import { UserType } from '~/types' + import type { staffUsersLogicType } from './staffUsersLogicType' export const staffUsersLogic = kea([ diff --git a/frontend/src/scenes/instance/SystemStatus/systemStatusLogic.ts b/frontend/src/scenes/instance/SystemStatus/systemStatusLogic.ts index 2a17736df79c8..a3fde5d7610ee 100644 --- a/frontend/src/scenes/instance/SystemStatus/systemStatusLogic.ts +++ b/frontend/src/scenes/instance/SystemStatus/systemStatusLogic.ts @@ -1,14 +1,16 @@ -import { actionToUrl, urlToAction } from 'kea-router' +import { actions, events, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' +import { actionToUrl, urlToAction } from 'kea-router' import api from 'lib/api' -import { kea, path, actions, reducers, selectors, listeners, events } from 'kea' -import type { systemStatusLogicType } from './systemStatusLogicType' -import { userLogic } from 'scenes/userLogic' -import { SystemStatus, SystemStatusRow, SystemStatusQueriesResult, InstanceSetting } from '~/types' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { isUserLoggedIn } from 'lib/utils' import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { isUserLoggedIn } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' + +import { InstanceSetting, SystemStatus, SystemStatusQueriesResult, SystemStatusRow } from '~/types' + +import type { systemStatusLogicType } from './systemStatusLogicType' export enum ConfigMode { View = 'view', diff --git a/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.tsx b/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.tsx index 594cfa583e4bc..a1f1fae053850 100644 --- a/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.tsx +++ b/frontend/src/scenes/notebooks/AddToNotebook/DraggableToNotebook.tsx @@ -1,13 +1,16 @@ -import React, { useState } from 'react' -import { NotebookNodeType } from '~/types' import './DraggableToNotebook.scss' -import { useActions, useValues } from 'kea' + import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { notebookPanelLogic } from '../NotebookPanel/notebookPanelLogic' +import React, { useState } from 'react' + +import { NotebookNodeType } from '~/types' + import { useNotebookNode } from '../Nodes/NotebookNodeContext' +import { notebookPanelLogic } from '../NotebookPanel/notebookPanelLogic' export type DraggableToNotebookBaseProps = { href?: string diff --git a/frontend/src/scenes/notebooks/IconNotebook.tsx b/frontend/src/scenes/notebooks/IconNotebook.tsx index a534044d3e6b7..bdc4a14becc46 100644 --- a/frontend/src/scenes/notebooks/IconNotebook.tsx +++ b/frontend/src/scenes/notebooks/IconNotebook.tsx @@ -1,6 +1,6 @@ +import { IconNotebook as IconNotebook3000 } from '@posthog/icons' import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' import { IconNotebook as IconNotebookLegacy, LemonIconProps } from 'lib/lemon-ui/icons' -import { IconNotebook as IconNotebook3000 } from '@posthog/icons' export function IconNotebook(props: LemonIconProps): JSX.Element { const is3000 = useFeatureFlag('POSTHOG_3000') diff --git a/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx b/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx index f50e1290734a2..5af0f8ed1d7d6 100644 --- a/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx +++ b/frontend/src/scenes/notebooks/Marks/NotebookMarkLink.tsx @@ -1,7 +1,8 @@ -import { Mark, getMarkRange, mergeAttributes } from '@tiptap/core' -import { linkPasteRule } from '../Nodes/utils' +import { getMarkRange, Mark, mergeAttributes } from '@tiptap/core' import { Plugin, PluginKey } from '@tiptap/pm/state' +import { linkPasteRule } from '../Nodes/utils' + export const NotebookMarkLink = Mark.create({ name: 'link', priority: 1000, diff --git a/frontend/src/scenes/notebooks/Nodes/NodeWrapper.scss b/frontend/src/scenes/notebooks/Nodes/NodeWrapper.scss index 179f475205dbf..1d4fa2d2021a3 100644 --- a/frontend/src/scenes/notebooks/Nodes/NodeWrapper.scss +++ b/frontend/src/scenes/notebooks/Nodes/NodeWrapper.scss @@ -56,7 +56,7 @@ } &--selected { - --border-color: var(--primary-3000); + --border-color: var(--border-bold); } &--auto-hide-metadata { diff --git a/frontend/src/scenes/notebooks/Nodes/NodeWrapper.tsx b/frontend/src/scenes/notebooks/Nodes/NodeWrapper.tsx index cba90e520677c..d2c96c06ee054 100644 --- a/frontend/src/scenes/notebooks/Nodes/NodeWrapper.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NodeWrapper.tsx @@ -75,7 +75,17 @@ function NodeWrapper(props: NodeWrapperP // nodeId can start null, but should then immediately be generated const nodeLogic = useMountedLogic(notebookNodeLogic(logicProps)) const { resizeable, expanded, actions, nodeId } = useValues(nodeLogic) - const { setExpanded, deleteNode, toggleEditing, insertOrSelectNextLine } = useActions(nodeLogic) + const { setRef, setExpanded, deleteNode, toggleEditing, insertOrSelectNextLine } = useActions(nodeLogic) + + const { ref: inViewRef, inView } = useInView({ triggerOnce: true }) + + const setRefs = useCallback( + (node) => { + setRef(node) + inViewRef(node) + }, + [inViewRef] + ) useEffect(() => { // TRICKY: child nodes mount the parent logic so we need to control the mounting / unmounting directly in this component @@ -92,7 +102,6 @@ function NodeWrapper(props: NodeWrapperP mountedNotebookLogic, }) - const [ref, inView] = useInView({ triggerOnce: true }) const contentRef = useRef(null) // If resizeable is true then the node attr "height" is required @@ -136,7 +145,7 @@ function NodeWrapper(props: NodeWrapperP
    { diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/NotebookNodePersonFeed.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/NotebookNodePersonFeed.tsx index b5c4ee5c2fd71..6b226f53c41ef 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/NotebookNodePersonFeed.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/NotebookNodePersonFeed.tsx @@ -1,10 +1,11 @@ -import { useValues } from 'kea' - import { LemonSkeleton } from '@posthog/lemon-ui' +import { useValues } from 'kea' import { NotFound } from 'lib/components/NotFound' -import { NotebookNodeType, PersonType } from '~/types' import { NotebookNodeProps } from 'scenes/notebooks/Notebook/utils' import { personLogic } from 'scenes/persons/personLogic' + +import { NotebookNodeType, PersonType } from '~/types' + import { createPostHogWidgetNode } from '../NodeWrapper' import { notebookNodePersonFeedLogic } from './notebookNodePersonFeedLogic' import { Session } from './Session' diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/Session.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/Session.tsx index d3f346ae2d728..799c9561c4180 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/Session.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/Session.tsx @@ -1,15 +1,16 @@ -import { useState } from 'react' -import { useActions, useValues } from 'kea' - -import { LemonButton } from '@posthog/lemon-ui' import { IconRewindPlay } from '@posthog/icons' +import { LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { dayjs } from 'lib/dayjs' -// import { TimelineEntry } from '~/queries/schema' -import { NotebookNodeType } from '~/types' import { IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' import { humanFriendlyDetailedTime, humanFriendlyDuration } from 'lib/utils' -import { SessionEvent } from './SessionEvent' +import { useState } from 'react' + +// import { TimelineEntry } from '~/queries/schema' +import { NotebookNodeType } from '~/types' + import { notebookNodeLogic } from '../notebookNodeLogic' +import { SessionEvent } from './SessionEvent' type SessionProps = { session: any // TimelineEntry diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/SessionEvent.tsx b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/SessionEvent.tsx index 00131544c5662..ddb16a428c1ce 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/SessionEvent.tsx +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/SessionEvent.tsx @@ -1,6 +1,8 @@ -import { EventType } from '~/types' -import { eventToDescription } from 'lib/utils' import { dayjs } from 'lib/dayjs' +import { eventToDescription } from 'lib/utils' + +import { EventType } from '~/types' + import { EventIcon } from './EventIcon' type SessionEventProps = { event: EventType } diff --git a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/notebookNodePersonFeedLogic.ts b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/notebookNodePersonFeedLogic.ts index 43649f6573a38..0f500ba932df8 100644 --- a/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/notebookNodePersonFeedLogic.ts +++ b/frontend/src/scenes/notebooks/Nodes/NotebookNodePersonFeed/notebookNodePersonFeedLogic.ts @@ -1,4 +1,4 @@ -import { kea, key, path, props, afterMount } from 'kea' +import { afterMount, kea, key, path, props } from 'kea' import { loaders } from 'kea-loaders' import { query } from '~/queries/query' diff --git a/frontend/src/scenes/notebooks/Nodes/components/NotebookNodeTitle.tsx b/frontend/src/scenes/notebooks/Nodes/components/NotebookNodeTitle.tsx index 4e7f6e2a2b045..33a473a06edea 100644 --- a/frontend/src/scenes/notebooks/Nodes/components/NotebookNodeTitle.tsx +++ b/frontend/src/scenes/notebooks/Nodes/components/NotebookNodeTitle.tsx @@ -1,10 +1,12 @@ -import { KeyboardEvent } from 'react' +import { LemonInput, Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { notebookNodeLogic } from '../notebookNodeLogic' +import posthog from 'posthog-js' +import { KeyboardEvent } from 'react' import { useEffect, useState } from 'react' -import { LemonInput, Tooltip } from '@posthog/lemon-ui' import { notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic' +import { notebookNodeLogic } from '../notebookNodeLogic' + export function NotebookNodeTitle(): JSX.Element { const { isEditable } = useValues(notebookLogic) const { nodeAttributes, title, titlePlaceholder } = useValues(notebookNodeLogic) @@ -21,6 +23,10 @@ export function NotebookNodeTitle(): JSX.Element { title: newValue ?? undefined, }) + if (title != newValue) { + posthog.capture('notebook node title updated') + } + setEditing(false) } @@ -42,7 +48,10 @@ export function NotebookNodeTitle(): JSX.Element { setEditing(true)} + onDoubleClick={() => { + setEditing(true) + posthog.capture('notebook editing node title') + }} > {title} diff --git a/frontend/src/scenes/notebooks/Nodes/notebookNodeLogic.ts b/frontend/src/scenes/notebooks/Nodes/notebookNodeLogic.ts index c40cdfbbdb781..e49cd38e1042d 100644 --- a/frontend/src/scenes/notebooks/Nodes/notebookNodeLogic.ts +++ b/frontend/src/scenes/notebooks/Nodes/notebookNodeLogic.ts @@ -63,6 +63,7 @@ export const notebookNodeLogic = kea([ initializeNode: true, setMessageListeners: (listeners: NotebookNodeMessagesListeners) => ({ listeners }), setTitlePlaceholder: (titlePlaceholder: string) => ({ titlePlaceholder }), + setRef: (ref: HTMLElement | null) => ({ ref }), }), connect((props: NotebookNodeLogicProps) => ({ @@ -71,6 +72,13 @@ export const notebookNodeLogic = kea([ })), reducers(({ props }) => ({ + ref: [ + null as HTMLElement | null, + { + setRef: (_, { ref }) => ref, + unregisterNodeLogic: () => null, + }, + ], expanded: [ props.startExpanded ?? true, { @@ -246,7 +254,9 @@ export const notebookNodeLogic = kea([ props.updateAttributes(attributes) }, toggleEditing: ({ visible }) => { - const shouldShowThis = typeof visible === 'boolean' ? visible : !values.notebookLogic.values.editingNodeId + const shouldShowThis = + typeof visible === 'boolean' ? visible : values.notebookLogic.values.editingNodeId !== values.nodeId + props.notebookLogic.actions.setEditingNodeId(shouldShowThis ? values.nodeId : null) }, initializeNode: () => { diff --git a/frontend/src/scenes/notebooks/Notebook/Editor.tsx b/frontend/src/scenes/notebooks/Notebook/Editor.tsx index be73659b7c7e0..287351779a53f 100644 --- a/frontend/src/scenes/notebooks/Notebook/Editor.tsx +++ b/frontend/src/scenes/notebooks/Notebook/Editor.tsx @@ -1,47 +1,46 @@ -import posthog from 'posthog-js' -import { useActions, useValues } from 'kea' -import { useCallback, useMemo, useRef } from 'react' - +import { lemonToast } from '@posthog/lemon-ui' import { Editor as TTEditor } from '@tiptap/core' -import { EditorContent, useEditor } from '@tiptap/react' +import ExtensionDocument from '@tiptap/extension-document' import { FloatingMenu } from '@tiptap/extension-floating-menu' -import StarterKit from '@tiptap/starter-kit' import ExtensionPlaceholder from '@tiptap/extension-placeholder' -import ExtensionDocument from '@tiptap/extension-document' import TaskItem from '@tiptap/extension-task-item' import TaskList from '@tiptap/extension-task-list' +import { EditorContent, useEditor } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import { useActions, useValues } from 'kea' +import { sampleOne } from 'lib/utils' +import posthog from 'posthog-js' +import { useCallback, useMemo, useRef } from 'react' -import { NotebookNodeFlagCodeExample } from '../Nodes/NotebookNodeFlagCodeExample' -import { NotebookNodeFlag } from '../Nodes/NotebookNodeFlag' -import { NotebookNodeExperiment } from '../Nodes/NotebookNodeExperiment' -import { NotebookNodeQuery } from '../Nodes/NotebookNodeQuery' -import { NotebookNodeRecording } from '../Nodes/NotebookNodeRecording' -import { NotebookNodePlaylist } from '../Nodes/NotebookNodePlaylist' -import { NotebookNodePerson } from '../Nodes/NotebookNodePerson' -import { NotebookNodeBacklink } from '../Nodes/NotebookNodeBacklink' -import { NotebookNodeReplayTimestamp } from '../Nodes/NotebookNodeReplayTimestamp' -import { NotebookMarkLink } from '../Marks/NotebookMarkLink' -import { insertionSuggestionsLogic } from '../Suggestions/insertionSuggestionsLogic' -import { FloatingSuggestions } from '../Suggestions/FloatingSuggestions' -import { lemonToast } from '@posthog/lemon-ui' import { NotebookNodeType } from '~/types' -import { NotebookNodeImage } from '../Nodes/NotebookNodeImage' -import { EditorFocusPosition, EditorRange, JSONContent, Node, textContent } from './utils' -import { SlashCommandsExtension } from './SlashCommands' +import { NotebookMarkLink } from '../Marks/NotebookMarkLink' +import { NotebookNodeBacklink } from '../Nodes/NotebookNodeBacklink' +import { NotebookNodeCohort } from '../Nodes/NotebookNodeCohort' import { NotebookNodeEarlyAccessFeature } from '../Nodes/NotebookNodeEarlyAccessFeature' -import { NotebookNodeSurvey } from '../Nodes/NotebookNodeSurvey' -import { InlineMenu } from './InlineMenu' -import { notebookLogic } from './notebookLogic' -import { sampleOne } from 'lib/utils' +import { NotebookNodeEmbed } from '../Nodes/NotebookNodeEmbed' +import { NotebookNodeExperiment } from '../Nodes/NotebookNodeExperiment' +import { NotebookNodeFlag } from '../Nodes/NotebookNodeFlag' +import { NotebookNodeFlagCodeExample } from '../Nodes/NotebookNodeFlagCodeExample' import { NotebookNodeGroup } from '../Nodes/NotebookNodeGroup' -import { NotebookNodeCohort } from '../Nodes/NotebookNodeCohort' +import { NotebookNodeImage } from '../Nodes/NotebookNodeImage' +import { NotebookNodeMap } from '../Nodes/NotebookNodeMap' +import { NotebookNodeMention } from '../Nodes/NotebookNodeMention' +import { NotebookNodePerson } from '../Nodes/NotebookNodePerson' import { NotebookNodePersonFeed } from '../Nodes/NotebookNodePersonFeed/NotebookNodePersonFeed' +import { NotebookNodePlaylist } from '../Nodes/NotebookNodePlaylist' import { NotebookNodeProperties } from '../Nodes/NotebookNodeProperties' -import { NotebookNodeMap } from '../Nodes/NotebookNodeMap' +import { NotebookNodeQuery } from '../Nodes/NotebookNodeQuery' +import { NotebookNodeRecording } from '../Nodes/NotebookNodeRecording' +import { NotebookNodeReplayTimestamp } from '../Nodes/NotebookNodeReplayTimestamp' +import { NotebookNodeSurvey } from '../Nodes/NotebookNodeSurvey' +import { FloatingSuggestions } from '../Suggestions/FloatingSuggestions' +import { insertionSuggestionsLogic } from '../Suggestions/insertionSuggestionsLogic' +import { InlineMenu } from './InlineMenu' import { MentionsExtension } from './MentionsExtension' -import { NotebookNodeMention } from '../Nodes/NotebookNodeMention' -import { NotebookNodeEmbed } from '../Nodes/NotebookNodeEmbed' +import { notebookLogic } from './notebookLogic' +import { SlashCommandsExtension } from './SlashCommands' +import { EditorFocusPosition, EditorRange, JSONContent, Node, textContent } from './utils' const CustomDocument = ExtensionDocument.extend({ content: 'heading block*', diff --git a/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx b/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx index 1eb952e410ee1..d0d31cd04377c 100644 --- a/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx +++ b/frontend/src/scenes/notebooks/Notebook/InlineMenu.tsx @@ -4,6 +4,7 @@ import { BubbleMenu } from '@tiptap/react' import { IconBold, IconDelete, IconItalic, IconLink, IconOpenInNew } from 'lib/lemon-ui/icons' import { isURL } from 'lib/utils' import { useRef } from 'react' + import NotebookIconHeading from './NotebookIconHeading' export const InlineMenu = ({ editor }: { editor: Editor }): JSX.Element => { diff --git a/frontend/src/scenes/notebooks/Notebook/MentionsExtension.tsx b/frontend/src/scenes/notebooks/Notebook/MentionsExtension.tsx index f18450d3b3525..d5c98de3f94ac 100644 --- a/frontend/src/scenes/notebooks/Notebook/MentionsExtension.tsx +++ b/frontend/src/scenes/notebooks/Notebook/MentionsExtension.tsx @@ -1,17 +1,18 @@ +import { LemonButton, ProfilePicture } from '@posthog/lemon-ui' import { Extension } from '@tiptap/core' -import Suggestion from '@tiptap/suggestion' - +import { PluginKey } from '@tiptap/pm/state' import { ReactRenderer } from '@tiptap/react' -import { LemonButton, ProfilePicture } from '@posthog/lemon-ui' -import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' -import { EditorRange } from './utils' -import { Popover } from 'lib/lemon-ui/Popover' +import Suggestion from '@tiptap/suggestion' import Fuse from 'fuse.js' import { useValues } from 'kea' -import { notebookLogic } from './notebookLogic' +import { Popover } from 'lib/lemon-ui/Popover' +import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' import { membersLogic } from 'scenes/organization/membersLogic' + import { NotebookNodeType, OrganizationMemberType } from '~/types' -import { PluginKey } from '@tiptap/pm/state' + +import { notebookLogic } from './notebookLogic' +import { EditorRange } from './utils' type MentionsProps = { range: EditorRange diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.scss b/frontend/src/scenes/notebooks/Notebook/Notebook.scss index ed584c90842d8..9b0139e499d63 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.scss +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.scss @@ -147,14 +147,6 @@ } } - &--editable { - .NotebookEditor .ProseMirror { - // Add some padding to help clicking below the last element - padding-bottom: 10rem; - flex: 1; - } - } - .NotebookColumn { position: relative; width: 0; @@ -191,6 +183,11 @@ .NotebookColumn__content { width: var(--notebook-column-left-width); transform: translateX(-100%); + + > .LemonWidget .LemonWidget__content { + max-height: var(--notebook-sidebar-height); + overflow: auto; + } } } @@ -218,12 +215,27 @@ } } + &--editable { + .NotebookEditor .ProseMirror { + // Add some padding to help clicking below the last element + padding-bottom: 10rem; + flex: 1; + } + + .NotebookColumn--left.NotebookColumn--showing { + & + .NotebookEditor { + .ProseMirror { + // Add a lot of padding to allow the entire column to always be on screen + padding-bottom: 100vh; + } + } + } + } + .NotebookHistory { flex: 1; display: flex; flex-direction: column; - height: var(--notebook-sidebar-height); - overflow: hidden; } .NotebookInlineMenu { @@ -236,13 +248,6 @@ } } - .NotebookColumnLeft__widget { - > .LemonWidget__content { - max-height: calc(100vh - 220px); - overflow: auto; - } - } - .LemonTable__content > table > thead { position: sticky; top: 0; diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx b/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx index 6e00e37d2652e..e67b51d7ad639 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.stories.tsx @@ -1,12 +1,14 @@ import { Meta, StoryFn } from '@storybook/react' -import { useEffect } from 'react' -import { mswDecorator } from '~/mocks/browser' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { useEffect } from 'react' import { App } from 'scenes/App' +import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' +import { NotebookType } from '~/types' + import notebook12345Json from './__mocks__/notebook-12345.json' import { notebookTestTemplate } from './__mocks__/notebook-template-for-snapshot' -import { NotebookType } from '~/types' // a list of test cases to run, showing different types of content in notebooks const testCases: Record = { diff --git a/frontend/src/scenes/notebooks/Notebook/Notebook.tsx b/frontend/src/scenes/notebooks/Notebook/Notebook.tsx index 9b65abb1ddf06..4b896c1cdb989 100644 --- a/frontend/src/scenes/notebooks/Notebook/Notebook.tsx +++ b/frontend/src/scenes/notebooks/Notebook/Notebook.tsx @@ -1,23 +1,25 @@ -import { useEffect } from 'react' -import { NotebookLogicProps, notebookLogic } from 'scenes/notebooks/Notebook/notebookLogic' -import { BindLogic, useActions, useValues } from 'kea' import './Notebook.scss' -import { NotFound } from 'lib/components/NotFound' import clsx from 'clsx' -import { notebookSettingsLogic } from './notebookSettingsLogic' +import { BindLogic, useActions, useValues } from 'kea' +import { NotFound } from 'lib/components/NotFound' +import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { useWhyDidIRender } from 'lib/hooks/useWhyDidIRender' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { useEffect } from 'react' +import { notebookLogic, NotebookLogicProps } from 'scenes/notebooks/Notebook/notebookLogic' + +import { ErrorBoundary } from '~/layout/ErrorBoundary' import { SCRATCHPAD_NOTEBOOK } from '~/models/notebooksModel' -import { NotebookConflictWarning } from './NotebookConflictWarning' -import { NotebookLoadingState } from './NotebookLoadingState' + import { Editor } from './Editor' -import { EditorFocusPosition, JSONContent } from './utils' import { NotebookColumnLeft } from './NotebookColumnLeft' -import { ErrorBoundary } from '~/layout/ErrorBoundary' -import { NotebookHistoryWarning } from './NotebookHistory' -import { useWhyDidIRender } from 'lib/hooks/useWhyDidIRender' import { NotebookColumnRight } from './NotebookColumnRight' -import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { NotebookConflictWarning } from './NotebookConflictWarning' +import { NotebookHistoryWarning } from './NotebookHistory' +import { NotebookLoadingState } from './NotebookLoadingState' +import { notebookSettingsLogic } from './notebookSettingsLogic' +import { EditorFocusPosition, JSONContent } from './utils' export type NotebookProps = NotebookLogicProps & { initialAutofocus?: EditorFocusPosition diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookColumnLeft.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookColumnLeft.tsx index e952c0cb8fd48..871f352760c89 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookColumnLeft.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookColumnLeft.tsx @@ -1,11 +1,12 @@ -import { LemonWidget } from 'lib/lemon-ui/LemonWidget' -import { BuiltLogic, useActions, useValues } from 'kea' +import { LemonButton } from '@posthog/lemon-ui' import clsx from 'clsx' -import { notebookLogic } from './notebookLogic' +import { BuiltLogic, useActions, useValues } from 'kea' +import { LemonWidget } from 'lib/lemon-ui/LemonWidget' +import { useEffect, useRef, useState } from 'react' + import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' -import { LemonButton } from '@posthog/lemon-ui' -import { IconEyeVisible } from 'lib/lemon-ui/icons' import { NotebookHistory } from './NotebookHistory' +import { notebookLogic } from './notebookLogic' export const NotebookColumnLeft = (): JSX.Element | null => { const { editingNodeLogic, isShowingLeftColumn, showHistory } = useValues(notebookLogic) @@ -16,7 +17,7 @@ export const NotebookColumnLeft = (): JSX.Element | null => { 'NotebookColumn--showing': isShowingLeftColumn, })} > -
    + {editingNodeLogic ? : null}
    {isShowingLeftColumn ? ( editingNodeLogic ? ( @@ -30,6 +31,41 @@ export const NotebookColumnLeft = (): JSX.Element | null => { ) } +export const NotebookNodeSettingsOffset = ({ logic }: { logic: BuiltLogic }): JSX.Element => { + const { ref } = useValues(logic) + const offsetRef = useRef(null) + const [height, setHeight] = useState(0) + + useEffect(() => { + // Interval to check the relative positions of the node and the offset div + // updating the height so that it always is inline + const updateHeight = (): void => { + if (ref && offsetRef.current) { + const newHeight = ref.getBoundingClientRect().top - offsetRef.current.getBoundingClientRect().top + + if (height !== newHeight) { + setHeight(newHeight) + } + } + } + + const interval = setInterval(updateHeight, 100) + updateHeight() + + return () => clearInterval(interval) + }, [ref, offsetRef.current, height]) + + return ( +
    + ) +} + export const NotebookNodeSettingsWidget = ({ logic }: { logic: BuiltLogic }): JSX.Element => { const { setEditingNodeId } = useActions(notebookLogic) const { Settings, nodeAttributes, title } = useValues(logic) @@ -41,16 +77,21 @@ export const NotebookNodeSettingsWidget = ({ logic }: { logic: BuiltLogic - } size="small" status="primary" onClick={() => selectNode()} /> setEditingNodeId(null)}> Done } > - {Settings ? ( - - ) : null} +
    selectNode()}> + {Settings ? ( + + ) : null} +
    ) } diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookColumnRight.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookColumnRight.tsx index 6ccb797affb76..82304503766f2 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookColumnRight.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookColumnRight.tsx @@ -1,10 +1,11 @@ -import { BuiltLogic, useValues } from 'kea' import clsx from 'clsx' -import { notebookLogic } from './notebookLogic' -import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' -import { NotebookNodeChildRenderer } from '../Nodes/NodeWrapper' +import { BuiltLogic, useValues } from 'kea' import { uuid } from 'lib/utils' +import { NotebookNodeChildRenderer } from '../Nodes/NodeWrapper' +import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' +import { notebookLogic } from './notebookLogic' + export const NotebookColumnRight = (): JSX.Element | null => { const { isShowingLeftColumn, nodeLogicsWithChildren } = useValues(notebookLogic) const isShowing = nodeLogicsWithChildren.length && !isShowingLeftColumn diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookConflictWarning.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookConflictWarning.tsx index 5151f746a87e9..e68db1137ea5b 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookConflictWarning.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookConflictWarning.tsx @@ -1,5 +1,6 @@ import { useActions } from 'kea' import { LemonButton } from 'lib/lemon-ui/LemonButton' + import { notebookLogic } from './notebookLogic' export function NotebookConflictWarning(): JSX.Element { diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookHistory.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookHistory.tsx index 5837493dc6c91..2ce11249bf535 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookHistory.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookHistory.tsx @@ -1,21 +1,22 @@ -import { useActions, useValues } from 'kea' -import { notebookLogic } from './notebookLogic' -import { ActivityLogItem, ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { TZLabel } from '@posthog/apps-common' import { LemonBanner, LemonButton, LemonSkeleton, + lemonToast, LemonWidget, PaginationControl, ProfilePicture, - lemonToast, usePagination, } from '@posthog/lemon-ui' import { JSONContent } from '@tiptap/core' +import { useActions, useValues } from 'kea' import { activityLogLogic } from 'lib/components/ActivityLog/activityLogLogic' -import { TZLabel } from '@posthog/apps-common' +import { ActivityLogItem, ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' import { useMemo } from 'react' +import { notebookLogic } from './notebookLogic' + const getFieldChange = (logItem: ActivityLogItem, field: string): any => { return logItem.detail.changes?.find((x) => x.field === field)?.after } diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookListMini.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookListMini.tsx index 987d55de03190..cc7f0e525e85e 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookListMini.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookListMini.tsx @@ -1,10 +1,12 @@ import { LemonButton } from '@posthog/lemon-ui' import { useValues } from 'kea' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' + import { notebooksModel } from '~/models/notebooksModel' import { NotebookListItemType } from '~/types' -import { NotebookSelectPopover } from '../NotebookSelectButton/NotebookSelectButton' + import { IconNotebook } from '../IconNotebook' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { NotebookSelectPopover } from '../NotebookSelectButton/NotebookSelectButton' export type NotebookListMiniProps = { selectedNotebookId?: string diff --git a/frontend/src/scenes/notebooks/Notebook/NotebookMeta.tsx b/frontend/src/scenes/notebooks/Notebook/NotebookMeta.tsx index a3da6c0b0c11c..ebd680cb47f35 100644 --- a/frontend/src/scenes/notebooks/Notebook/NotebookMeta.tsx +++ b/frontend/src/scenes/notebooks/Notebook/NotebookMeta.tsx @@ -1,11 +1,13 @@ -import { NotebookLogicProps, notebookLogic } from './notebookLogic' +import { LemonButton, LemonButtonProps } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { IconDocumentExpand } from 'lib/lemon-ui/icons' import { Spinner } from 'lib/lemon-ui/Spinner' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { useActions, useValues } from 'kea' import { useCallback, useEffect, useState } from 'react' + import { NotebookSyncStatus } from '~/types' -import { LemonButton, LemonButtonProps } from '@posthog/lemon-ui' -import { IconDocumentExpand } from 'lib/lemon-ui/icons' + +import { notebookLogic, NotebookLogicProps } from './notebookLogic' import { notebookSettingsLogic } from './notebookSettingsLogic' const syncStatusMap: Record = { diff --git a/frontend/src/scenes/notebooks/Notebook/SlashCommands.tsx b/frontend/src/scenes/notebooks/Notebook/SlashCommands.tsx index 6fb6e14c67654..0a7bf012d8e19 100644 --- a/frontend/src/scenes/notebooks/Notebook/SlashCommands.tsx +++ b/frontend/src/scenes/notebooks/Notebook/SlashCommands.tsx @@ -1,14 +1,9 @@ -import { Extension } from '@tiptap/core' -import Suggestion from '@tiptap/suggestion' - -import { ReactRenderer } from '@tiptap/react' -import { LemonButton, LemonDivider, lemonToast } from '@posthog/lemon-ui' -import { IconBold, IconCohort, IconItalic } from 'lib/lemon-ui/icons' import { IconCursor, IconFunnels, IconHogQL, IconLifecycle, + IconPeople, IconRetention, IconRewindPlay, IconStickiness, @@ -16,21 +11,28 @@ import { IconUpload, IconUserPaths, } from '@posthog/icons' -import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' -import { EditorCommands, EditorRange } from './utils' -import { BaseMathType, ChartDisplayType, FunnelVizType, NotebookNodeType, PathType, RetentionPeriod } from '~/types' -import { Popover } from 'lib/lemon-ui/Popover' -import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' +import { IconCode } from '@posthog/icons' +import { LemonButton, LemonDivider, lemonToast } from '@posthog/lemon-ui' +import { Extension } from '@tiptap/core' +import { ReactRenderer } from '@tiptap/react' +import Suggestion from '@tiptap/suggestion' import Fuse from 'fuse.js' import { useValues } from 'kea' -import { notebookLogic } from './notebookLogic' -import { selectFile } from '../Nodes/utils' -import NotebookIconHeading from './NotebookIconHeading' -import { NodeKind } from '~/queries/schema' +import { IconBold, IconItalic } from 'lib/lemon-ui/icons' +import { Popover } from 'lib/lemon-ui/Popover' +import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react' + +import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' -import { buildInsightVizQueryContent, buildNodeQueryContent } from '../Nodes/NotebookNodeQuery' +import { NodeKind } from '~/queries/schema' +import { BaseMathType, ChartDisplayType, FunnelVizType, NotebookNodeType, PathType, RetentionPeriod } from '~/types' + import { buildNodeEmbed } from '../Nodes/NotebookNodeEmbed' -import { IconCode } from '@posthog/icons' +import { buildInsightVizQueryContent, buildNodeQueryContent } from '../Nodes/NotebookNodeQuery' +import { selectFile } from '../Nodes/utils' +import NotebookIconHeading from './NotebookIconHeading' +import { notebookLogic } from './notebookLogic' +import { EditorCommands, EditorRange } from './utils' type SlashCommandConditionalProps = | { @@ -97,7 +99,7 @@ const TEXT_CONTROLS: SlashCommandsItem[] = [ const SLASH_COMMANDS: SlashCommandsItem[] = [ { title: 'Trend', - search: 'trend insight', + search: 'graph trend insight', icon: , command: (chain, pos) => chain.insertContentAt( @@ -176,7 +178,7 @@ const SLASH_COMMANDS: SlashCommandsItem[] = [ }, { title: 'Paths', - search: 'paths insight', + search: 'user paths insight', icon: , command: (chain, pos) => chain.insertContentAt( @@ -282,9 +284,9 @@ order by count() desc ), }, { - title: 'Persons', - search: 'people users', - icon: , + title: 'People', + search: 'persons users', + icon: , command: (chain, pos) => chain.insertContentAt( pos, @@ -299,14 +301,14 @@ order by count() desc ), }, { - title: 'Session Replays', - search: 'recordings video', + title: 'Session recordings', + search: 'video replay', icon: , command: (chain, pos) => chain.insertContentAt(pos, { type: NotebookNodeType.RecordingPlaylist, attrs: {} }), }, { title: 'Image', - search: 'picture', + search: 'picture gif', icon: , command: async (chain, pos) => { // Trigger upload followed by insert diff --git a/frontend/src/scenes/notebooks/Notebook/__mocks__/notebook-template-for-snapshot.ts b/frontend/src/scenes/notebooks/Notebook/__mocks__/notebook-template-for-snapshot.ts index b87917836a5db..56c0cbe4586af 100644 --- a/frontend/src/scenes/notebooks/Notebook/__mocks__/notebook-template-for-snapshot.ts +++ b/frontend/src/scenes/notebooks/Notebook/__mocks__/notebook-template-for-snapshot.ts @@ -1,7 +1,8 @@ -import { NotebookType } from '~/types' import { MOCK_DEFAULT_BASIC_USER } from 'lib/api.mock' import { JSONContent } from 'scenes/notebooks/Notebook/utils' +import { NotebookType } from '~/types' + export const notebookTestTemplate = ( title: string = 'Notebook for snapshots', notebookJson: JSONContent[] diff --git a/frontend/src/scenes/notebooks/Notebook/migrations/migrate.ts b/frontend/src/scenes/notebooks/Notebook/migrations/migrate.ts index 80ec5b615fdb7..bd589a5803573 100644 --- a/frontend/src/scenes/notebooks/Notebook/migrations/migrate.ts +++ b/frontend/src/scenes/notebooks/Notebook/migrations/migrate.ts @@ -1,4 +1,5 @@ import { JSONContent } from '@tiptap/core' + import { NodeKind } from '~/queries/schema' import { NotebookNodeType, NotebookType } from '~/types' diff --git a/frontend/src/scenes/notebooks/Notebook/notebookLogic.ts b/frontend/src/scenes/notebooks/Notebook/notebookLogic.ts index 0ffea49bab2d3..fd825533f93aa 100644 --- a/frontend/src/scenes/notebooks/Notebook/notebookLogic.ts +++ b/frontend/src/scenes/notebooks/Notebook/notebookLogic.ts @@ -1,6 +1,7 @@ +import { lemonToast } from '@posthog/lemon-ui' import { - BuiltLogic, actions, + BuiltLogic, connect, kea, key, @@ -11,25 +12,25 @@ import { selectors, sharedListeners, } from 'kea' -import type { notebookLogicType } from './notebookLogicType' import { loaders } from 'kea-loaders' -import { notebooksModel, openNotebook, SCRATCHPAD_NOTEBOOK } from '~/models/notebooksModel' -import { NotebookNodeType, NotebookSyncStatus, NotebookTarget, NotebookType } from '~/types' - -// NOTE: Annoyingly, if we import this then kea logic type-gen generates -// two imports and fails so, we reimport it from a utils file -import { EditorRange, JSONContent, NotebookEditor } from './utils' +import { router, urlToAction } from 'kea-router' import api from 'lib/api' -import posthog from 'posthog-js' import { downloadFile, slugify } from 'lib/utils' -import { lemonToast } from '@posthog/lemon-ui' -import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' +import posthog from 'posthog-js' import { buildTimestampCommentContent, NotebookNodeReplayTimestampAttrs, } from 'scenes/notebooks/Nodes/NotebookNodeReplayTimestamp' -import { NOTEBOOKS_VERSION, migrate } from './migrations/migrate' -import { router, urlToAction } from 'kea-router' + +import { notebooksModel, openNotebook, SCRATCHPAD_NOTEBOOK } from '~/models/notebooksModel' +import { NotebookNodeType, NotebookSyncStatus, NotebookTarget, NotebookType } from '~/types' + +import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' +import { migrate, NOTEBOOKS_VERSION } from './migrations/migrate' +import type { notebookLogicType } from './notebookLogicType' +// NOTE: Annoyingly, if we import this then kea logic type-gen generates +// two imports and fails so, we reimport it from a utils file +import { EditorRange, JSONContent, NotebookEditor } from './utils' const SYNC_DELAY = 1000 diff --git a/frontend/src/scenes/notebooks/Notebook/utils.ts b/frontend/src/scenes/notebooks/Notebook/utils.ts index 2a989fd8a8299..c084ecad319a4 100644 --- a/frontend/src/scenes/notebooks/Notebook/utils.ts +++ b/frontend/src/scenes/notebooks/Notebook/utils.ts @@ -1,18 +1,20 @@ // Helpers for Kea issue with double importing import { LemonButtonProps } from '@posthog/lemon-ui' import { + Attribute, ChainedCommands as EditorCommands, Editor as TTEditor, + ExtendedRegExpMatchArray, FocusPosition as EditorFocusPosition, getText, JSONContent as TTJSONContent, Range as EditorRange, TextSerializer, - ExtendedRegExpMatchArray, - Attribute, } from '@tiptap/core' import { Node as PMNode } from '@tiptap/pm/model' + import { NotebookNodeResource, NotebookNodeType } from '~/types' + import { NotebookNodeLogicProps } from '../Nodes/notebookNodeLogic' // TODO: fix the typing of string to NotebookNodeType @@ -53,8 +55,8 @@ export interface JSONContent extends TTJSONContent {} export { ChainedCommands as EditorCommands, - Range as EditorRange, FocusPosition as EditorFocusPosition, + Range as EditorRange, } from '@tiptap/core' export type CustomNotebookNodeAttributes = Record diff --git a/frontend/src/scenes/notebooks/NotebookCanvasScene.tsx b/frontend/src/scenes/notebooks/NotebookCanvasScene.tsx index 894d00344a1b8..e6db7608942d4 100644 --- a/frontend/src/scenes/notebooks/NotebookCanvasScene.tsx +++ b/frontend/src/scenes/notebooks/NotebookCanvasScene.tsx @@ -1,13 +1,15 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { NotebookLogicProps, notebookLogic } from './Notebook/notebookLogic' -import { Notebook } from './Notebook/Notebook' import './NotebookScene.scss' -import { useMemo } from 'react' -import { uuid } from 'lib/utils' -import { useActions } from 'kea' + import { LemonBanner } from '@posthog/lemon-ui' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { useActions } from 'kea' import { NotFound } from 'lib/components/NotFound' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { uuid } from 'lib/utils' +import { useMemo } from 'react' +import { SceneExport } from 'scenes/sceneTypes' + +import { Notebook } from './Notebook/Notebook' +import { notebookLogic, NotebookLogicProps } from './Notebook/notebookLogic' export const scene: SceneExport = { component: NotebookCanvas, diff --git a/frontend/src/scenes/notebooks/NotebookMenu.tsx b/frontend/src/scenes/notebooks/NotebookMenu.tsx index ea307f726c8b9..b604271643f1a 100644 --- a/frontend/src/scenes/notebooks/NotebookMenu.tsx +++ b/frontend/src/scenes/notebooks/NotebookMenu.tsx @@ -1,12 +1,15 @@ -import { useActions, useValues } from 'kea' -import { NotebookLogicProps, notebookLogic } from './Notebook/notebookLogic' +import './NotebookScene.scss' + import { LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { router } from 'kea-router' import { IconDelete, IconEllipsis, IconExport, IconNotification, IconShare } from 'lib/lemon-ui/icons' import { LemonMenu } from 'lib/lemon-ui/LemonMenu' -import { notebooksModel } from '~/models/notebooksModel' -import { router } from 'kea-router' import { urls } from 'scenes/urls' -import './NotebookScene.scss' + +import { notebooksModel } from '~/models/notebooksModel' + +import { notebookLogic, NotebookLogicProps } from './Notebook/notebookLogic' import { openNotebookShareDialog } from './Notebook/NotebookShare' export function NotebookMenu({ shortId }: NotebookLogicProps): JSX.Element { diff --git a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.tsx b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.tsx index ef4503e51a8c4..8392f5dc9f2cf 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.tsx +++ b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanel.tsx @@ -1,19 +1,22 @@ -import { useActions, useValues } from 'kea' import './NotebookPanel.scss' -import { Notebook } from '../Notebook/Notebook' + import { LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' import { IconOpenInNew } from 'lib/lemon-ui/icons' import { useMemo } from 'react' +import { urls } from 'scenes/urls' + +import { SidePanelPaneHeader } from '~/layout/navigation-3000/sidepanel/components/SidePanelPane' +import { NotebookTarget } from '~/types' + +import { Notebook } from '../Notebook/Notebook' import { NotebookListMini } from '../Notebook/NotebookListMini' -import { NotebookExpandButton, NotebookSyncInfo } from '../Notebook/NotebookMeta' import { notebookLogic } from '../Notebook/notebookLogic' -import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { notebookPanelLogic } from './notebookPanelLogic' -import { NotebookPanelDropzone } from './NotebookPanelDropzone' -import { urls } from 'scenes/urls' +import { NotebookExpandButton, NotebookSyncInfo } from '../Notebook/NotebookMeta' import { NotebookMenu } from '../NotebookMenu' -import { NotebookTarget } from '~/types' -import { SidePanelPaneHeader } from '~/layout/navigation-3000/sidepanel/components/SidePanelPane' +import { NotebookPanelDropzone } from './NotebookPanelDropzone' +import { notebookPanelLogic } from './notebookPanelLogic' export function NotebookPanel(): JSX.Element | null { const { selectedNotebook, initialAutofocus, droppedResource, dropProperties } = useValues(notebookPanelLogic) diff --git a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanelDropzone.tsx b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanelDropzone.tsx index 4f4167486e22e..2e219c197fc98 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanelDropzone.tsx +++ b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPanelDropzone.tsx @@ -1,10 +1,12 @@ +import { LemonButton } from '@posthog/lemon-ui' import clsx from 'clsx' -import { DragEventHandler, useState } from 'react' import { useActions, useValues } from 'kea' +import { DragEventHandler, useState } from 'react' + import { NotebookNodeType } from '~/types' -import { NotebookSelectList } from '../NotebookSelectButton/NotebookSelectButton' + import { notebookLogicType } from '../Notebook/notebookLogicType' -import { LemonButton } from '@posthog/lemon-ui' +import { NotebookSelectList } from '../NotebookSelectButton/NotebookSelectButton' import { notebookPanelLogic } from './notebookPanelLogic' export function NotebookPanelDropzone(): JSX.Element | null { diff --git a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPopover.tsx b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPopover.tsx index 9775a3a0a7406..a10016e0eb5cc 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/NotebookPopover.tsx +++ b/frontend/src/scenes/notebooks/NotebookPanel/NotebookPopover.tsx @@ -1,21 +1,23 @@ -import { useActions, useValues } from 'kea' -import clsx from 'clsx' import './NotebookPopover.scss' -import { Notebook } from '../Notebook/Notebook' -import { notebookPopoverLogic } from 'scenes/notebooks/NotebookPanel/notebookPopoverLogic' + import { LemonButton } from '@posthog/lemon-ui' -import { IconFullScreen, IconChevronRight, IconOpenInNew, IconShare } from 'lib/lemon-ui/icons' -import { useEffect, useMemo, useRef } from 'react' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' -import { NotebookListMini } from '../Notebook/NotebookListMini' -import { NotebookExpandButton, NotebookSyncInfo } from '../Notebook/NotebookMeta' -import { notebookLogic } from '../Notebook/notebookLogic' -import { urls } from 'scenes/urls' -import { NotebookPanelDropzone } from './NotebookPanelDropzone' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { openNotebookShareDialog } from '../Notebook/NotebookShare' +import { IconChevronRight, IconFullScreen, IconOpenInNew, IconShare } from 'lib/lemon-ui/icons' +import { useEffect, useMemo, useRef } from 'react' +import { notebookPopoverLogic } from 'scenes/notebooks/NotebookPanel/notebookPopoverLogic' import { sceneLogic } from 'scenes/sceneLogic' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Notebook } from '../Notebook/Notebook' +import { NotebookListMini } from '../Notebook/NotebookListMini' +import { notebookLogic } from '../Notebook/notebookLogic' +import { NotebookExpandButton, NotebookSyncInfo } from '../Notebook/NotebookMeta' +import { openNotebookShareDialog } from '../Notebook/NotebookShare' +import { NotebookPanelDropzone } from './NotebookPanelDropzone' export function NotebookPopoverCard(): JSX.Element | null { const { popoverVisibility, shownAtLeastOnce, fullScreen, selectedNotebook, initialAutofocus, droppedResource } = diff --git a/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts b/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts index 0f18ce5290ece..7f77123b42c53 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts +++ b/frontend/src/scenes/notebooks/NotebookPanel/notebookPanelLogic.ts @@ -1,14 +1,14 @@ -import { actions, kea, reducers, path, listeners, selectors, connect } from 'kea' - +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { HTMLProps } from 'react' -import { EditorFocusPosition } from '../Notebook/utils' -import type { notebookPanelLogicType } from './notebookPanelLogicType' +import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic' import { NotebookNodeResource, SidePanelTab } from '~/types' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' + +import { EditorFocusPosition } from '../Notebook/utils' +import type { notebookPanelLogicType } from './notebookPanelLogicType' import { notebookPopoverLogic } from './notebookPopoverLogic' -import { sidePanelStateLogic } from '~/layout/navigation-3000/sidepanel/sidePanelStateLogic' export const notebookPanelLogic = kea([ path(['scenes', 'notebooks', 'Notebook', 'notebookPanelLogic']), diff --git a/frontend/src/scenes/notebooks/NotebookPanel/notebookPopoverLogic.ts b/frontend/src/scenes/notebooks/NotebookPanel/notebookPopoverLogic.ts index 471da0ed75e8b..d1a0997d2179e 100644 --- a/frontend/src/scenes/notebooks/NotebookPanel/notebookPopoverLogic.ts +++ b/frontend/src/scenes/notebooks/NotebookPanel/notebookPopoverLogic.ts @@ -1,14 +1,14 @@ -import { actions, kea, reducers, path, listeners, selectors } from 'kea' - +import { actions, kea, listeners, path, reducers, selectors } from 'kea' import { urlToAction } from 'kea-router' -import { HTMLProps, RefObject } from 'react' -import posthog from 'posthog-js' import { subscriptions } from 'kea-subscriptions' -import { EditorFocusPosition } from '../Notebook/utils' +import posthog from 'posthog-js' +import { HTMLProps, RefObject } from 'react' -import type { notebookPopoverLogicType } from './notebookPopoverLogicType' import { NotebookNodeResource, NotebookPopoverVisibility } from '~/types' +import { EditorFocusPosition } from '../Notebook/utils' +import type { notebookPopoverLogicType } from './notebookPopoverLogicType' + export const notebookPopoverLogic = kea([ path(['scenes', 'notebooks', 'Notebook', 'notebookPopoverLogic']), actions({ diff --git a/frontend/src/scenes/notebooks/NotebookScene.tsx b/frontend/src/scenes/notebooks/NotebookScene.tsx index a6a4ad229270f..0d0b2baa69f5e 100644 --- a/frontend/src/scenes/notebooks/NotebookScene.tsx +++ b/frontend/src/scenes/notebooks/NotebookScene.tsx @@ -1,21 +1,24 @@ +import './NotebookScene.scss' + +import { IconInfo, IconOpenSidebar } from '@posthog/icons' +import { LemonButton, LemonTag } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { SceneExport } from 'scenes/sceneTypes' -import { notebookLogic } from './Notebook/notebookLogic' -import { Notebook } from './Notebook/Notebook' import { NotFound } from 'lib/components/NotFound' -import { NotebookSceneLogicProps, notebookSceneLogic } from './notebookSceneLogic' -import { LemonButton, LemonTag } from '@posthog/lemon-ui' -import { NotebookExpandButton, NotebookSyncInfo } from './Notebook/NotebookMeta' import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' -import { IconArrowRight, IconHelpOutline } from 'lib/lemon-ui/icons' -import { LOCAL_NOTEBOOK_TEMPLATES } from './NotebookTemplates/notebookTemplates' -import './NotebookScene.scss' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { NotebookTarget } from '~/types' + +import { Notebook } from './Notebook/Notebook' import { NotebookLoadingState } from './Notebook/NotebookLoadingState' -import { notebookPanelLogic } from './NotebookPanel/notebookPanelLogic' +import { notebookLogic } from './Notebook/notebookLogic' +import { NotebookExpandButton, NotebookSyncInfo } from './Notebook/NotebookMeta' import { NotebookMenu } from './NotebookMenu' -import { NotebookTarget } from '~/types' +import { notebookPanelLogic } from './NotebookPanel/notebookPanelLogic' +import { notebookSceneLogic, NotebookSceneLogicProps } from './notebookSceneLogic' +import { LOCAL_NOTEBOOK_TEMPLATES } from './NotebookTemplates/notebookTemplates' interface NotebookSceneProps { shortId?: string @@ -48,7 +51,7 @@ export function NotebookScene(): JSX.Element { return (

    - This Notebook is open in the side panel + This Notebook is open in the side panel

    @@ -84,7 +87,7 @@ export function NotebookScene(): JSX.Element { } + icon={} size={buttonSize} onClick={() => { if (selectedNotebook === LOCAL_NOTEBOOK_TEMPLATES[0].short_id && visibility === 'visible') { @@ -109,11 +112,11 @@ export function NotebookScene(): JSX.Element { tooltip={ <> Opens the notebook in a side panel, that can be accessed from anywhere in the PostHog - app. This is great for dragging and dropping elements like Insights, Recordings or even - Feature Flags into your active Notebook. + app. This is great for dragging and dropping elements like insights, recordings or even + feature flags into your active notebook. } - sideIcon={} + sideIcon={} > Open in side panel diff --git a/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.stories.tsx b/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.stories.tsx index a68a60d9b5bdc..d7bf1039a7f9a 100644 --- a/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.stories.tsx +++ b/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.stories.tsx @@ -1,8 +1,9 @@ import { Meta, StoryFn } from '@storybook/react' +import { FEATURE_FLAGS } from 'lib/constants' import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' + import { setFeatureFlags, useStorybookMocks } from '~/mocks/browser' import { NotebookNodeType } from '~/types' -import { FEATURE_FLAGS } from 'lib/constants' export default { title: 'Scenes-App/Notebooks/Components/Notebook Select Button', diff --git a/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.tsx b/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.tsx index 7d313a160ec59..63e8f2b501a9a 100644 --- a/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.tsx +++ b/frontend/src/scenes/notebooks/NotebookSelectButton/NotebookSelectButton.tsx @@ -1,24 +1,25 @@ -import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' - +import { LemonDivider, ProfilePicture } from '@posthog/lemon-ui' +import { BuiltLogic, useActions, useValues } from 'kea' +import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { FEATURE_FLAGS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' import { IconPlus, IconWithCount } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { Popover, PopoverProps } from 'lib/lemon-ui/Popover' +import { ReactChild, useEffect } from 'react' +import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' import { - NotebookSelectButtonLogicProps, notebookSelectButtonLogic, + NotebookSelectButtonLogicProps, } from 'scenes/notebooks/NotebookSelectButton/notebookSelectButtonLogic' -import { BuiltLogic, useActions, useValues } from 'kea' -import { dayjs } from 'lib/dayjs' -import { NotebookListItemType, NotebookTarget } from '~/types' + import { notebooksModel, openNotebook } from '~/models/notebooksModel' -import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' -import { Popover, PopoverProps } from 'lib/lemon-ui/Popover' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { notebookLogicType } from '../Notebook/notebookLogicType' -import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' -import { FlaggedFeature } from 'lib/components/FlaggedFeature' -import { FEATURE_FLAGS } from 'lib/constants' -import { ReactChild, useEffect } from 'react' -import { LemonDivider, ProfilePicture } from '@posthog/lemon-ui' +import { NotebookListItemType, NotebookTarget } from '~/types' + import { IconNotebook } from '../IconNotebook' +import { notebookNodeLogicType } from '../Nodes/notebookNodeLogicType' +import { notebookLogicType } from '../Notebook/notebookLogicType' export type NotebookSelectProps = NotebookSelectButtonLogicProps & { newNotebookTitle?: string diff --git a/frontend/src/scenes/notebooks/NotebookSelectButton/notebookSelectButtonLogic.ts b/frontend/src/scenes/notebooks/NotebookSelectButton/notebookSelectButtonLogic.ts index ed446007c031d..956a96094fad7 100644 --- a/frontend/src/scenes/notebooks/NotebookSelectButton/notebookSelectButtonLogic.ts +++ b/frontend/src/scenes/notebooks/NotebookSelectButton/notebookSelectButtonLogic.ts @@ -1,9 +1,9 @@ import { actions, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { NotebookListItemType, NotebookNodeResource, NotebookNodeType } from '~/types' - import api from 'lib/api' +import { NotebookListItemType, NotebookNodeResource, NotebookNodeType } from '~/types' + import type { notebookSelectButtonLogicType } from './notebookSelectButtonLogicType' export interface NotebookSelectButtonLogicProps { diff --git a/frontend/src/scenes/notebooks/NotebooksScene.tsx b/frontend/src/scenes/notebooks/NotebooksScene.tsx index 7caede1f116db..df4360e389353 100644 --- a/frontend/src/scenes/notebooks/NotebooksScene.tsx +++ b/frontend/src/scenes/notebooks/NotebooksScene.tsx @@ -1,10 +1,12 @@ -import { SceneExport } from 'scenes/sceneTypes' import './NotebookScene.scss' -import { NotebooksTable } from './NotebooksTable/NotebooksTable' + import { LemonButton, LemonTag } from '@posthog/lemon-ui' import { PageHeader } from 'lib/components/PageHeader' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' +import { NotebooksTable } from './NotebooksTable/NotebooksTable' + export const scene: SceneExport = { component: NotebooksScene, } diff --git a/frontend/src/scenes/notebooks/NotebooksTable/ContainsTypeFilter.tsx b/frontend/src/scenes/notebooks/NotebooksTable/ContainsTypeFilter.tsx index 1a8c2f3159b86..94fcfaa5572c9 100644 --- a/frontend/src/scenes/notebooks/NotebooksTable/ContainsTypeFilter.tsx +++ b/frontend/src/scenes/notebooks/NotebooksTable/ContainsTypeFilter.tsx @@ -1,7 +1,9 @@ -import { NotebookNodeType } from '~/types' import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple' +import posthog from 'posthog-js' import { NotebooksListFilters } from 'scenes/notebooks/NotebooksTable/notebooksTableLogic' +import { NotebookNodeType } from '~/types' + export const fromNodeTypeToLabel: Omit< Record, | NotebookNodeType.Backlink @@ -48,6 +50,7 @@ export function ContainsTypeFilters({ }, {})} value={filters.contains} onChange={(newValue: string[]) => { + posthog.capture('notebook containing filter applied') setFilters({ contains: newValue.map((x) => x as NotebookNodeType) }) }} data-attr={'notebooks-list-contains-filters'} diff --git a/frontend/src/scenes/notebooks/NotebooksTable/NotebooksTable.tsx b/frontend/src/scenes/notebooks/NotebooksTable/NotebooksTable.tsx index 1a2a5ddba8919..bb10007b72fd9 100644 --- a/frontend/src/scenes/notebooks/NotebooksTable/NotebooksTable.tsx +++ b/frontend/src/scenes/notebooks/NotebooksTable/NotebooksTable.tsx @@ -1,18 +1,20 @@ +import { LemonButton, LemonInput, LemonSelect, LemonTag } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { IconDelete, IconEllipsis } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonMenu } from 'lib/lemon-ui/LemonMenu' import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { NotebookListItemType } from '~/types' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' -import { LemonButton, LemonInput, LemonSelect, LemonTag } from '@posthog/lemon-ui' -import { notebooksModel } from '~/models/notebooksModel' +import { Link } from 'lib/lemon-ui/Link' import { useEffect } from 'react' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonMenu } from 'lib/lemon-ui/LemonMenu' -import { IconDelete, IconEllipsis } from 'lib/lemon-ui/icons' -import { membersLogic } from 'scenes/organization/membersLogic' import { ContainsTypeFilters } from 'scenes/notebooks/NotebooksTable/ContainsTypeFilter' import { DEFAULT_FILTERS, notebooksTableLogic } from 'scenes/notebooks/NotebooksTable/notebooksTableLogic' +import { membersLogic } from 'scenes/organization/membersLogic' +import { urls } from 'scenes/urls' + +import { notebooksModel } from '~/models/notebooksModel' +import { NotebookListItemType } from '~/types' + import { notebookPanelLogic } from '../NotebookPanel/notebookPanelLogic' function titleColumn(): LemonTableColumn { diff --git a/frontend/src/scenes/notebooks/NotebooksTable/notebooksTableLogic.ts b/frontend/src/scenes/notebooks/NotebooksTable/notebooksTableLogic.ts index 8bc07b7f2bee8..b234af21f1270 100644 --- a/frontend/src/scenes/notebooks/NotebooksTable/notebooksTableLogic.ts +++ b/frontend/src/scenes/notebooks/NotebooksTable/notebooksTableLogic.ts @@ -1,11 +1,12 @@ -import { actions, kea, listeners, reducers, path, selectors, connect } from 'kea' -import { NotebookListItemType, NotebookNodeType } from '~/types' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import api from 'lib/api' import { objectClean, objectsEqual } from 'lib/utils' -import { loaders } from 'kea-loaders' -import type { notebooksTableLogicType } from './notebooksTableLogicType' import { notebooksModel } from '~/models/notebooksModel' +import { NotebookListItemType, NotebookNodeType } from '~/types' + +import type { notebooksTableLogicType } from './notebooksTableLogicType' export interface NotebooksListFilters { search: string diff --git a/frontend/src/scenes/notebooks/Suggestions/FloatingSuggestions.tsx b/frontend/src/scenes/notebooks/Suggestions/FloatingSuggestions.tsx index f5c17eca5d90c..42a557eb7ab91 100644 --- a/frontend/src/scenes/notebooks/Suggestions/FloatingSuggestions.tsx +++ b/frontend/src/scenes/notebooks/Suggestions/FloatingSuggestions.tsx @@ -1,11 +1,13 @@ import './FloatingSuggestions.scss' + import { Editor as TTEditor } from '@tiptap/core' import { useActions, useValues } from 'kea' -import { insertionSuggestionsLogic } from './insertionSuggestionsLogic' -import { isCurrentNodeEmpty } from '../Notebook/utils' +import { useResizeObserver } from 'lib/hooks/useResizeObserver' import { useEffect, useState } from 'react' + import { notebookLogic } from '../Notebook/notebookLogic' -import { useResizeObserver } from 'lib/hooks/useResizeObserver' +import { isCurrentNodeEmpty } from '../Notebook/utils' +import { insertionSuggestionsLogic } from './insertionSuggestionsLogic' export function FloatingSuggestions({ editor }: { editor: TTEditor }): JSX.Element | null { const logic = insertionSuggestionsLogic() diff --git a/frontend/src/scenes/notebooks/Suggestions/ReplayTimestamp.tsx b/frontend/src/scenes/notebooks/Suggestions/ReplayTimestamp.tsx index ce7c762d7bd66..451dd067d7d9c 100644 --- a/frontend/src/scenes/notebooks/Suggestions/ReplayTimestamp.tsx +++ b/frontend/src/scenes/notebooks/Suggestions/ReplayTimestamp.tsx @@ -1,12 +1,14 @@ +import { LemonButton } from '@posthog/lemon-ui' +import { useValues } from 'kea' +import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' + import { NotebookNodeType } from '~/types' -import { firstChildOfType, hasChildOfType } from '../Notebook/Editor' -import { buildTimestampCommentContent, formatTimestamp } from '../Nodes/NotebookNodeReplayTimestamp' + import { sessionRecordingPlayerProps } from '../Nodes/NotebookNodeRecording' -import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { useValues } from 'kea' -import { InsertionSuggestion, InsertionSuggestionViewProps } from './InsertionSuggestion' +import { buildTimestampCommentContent, formatTimestamp } from '../Nodes/NotebookNodeReplayTimestamp' +import { firstChildOfType, hasChildOfType } from '../Notebook/Editor' import { Node, NotebookEditor } from '../Notebook/utils' -import { LemonButton } from '@posthog/lemon-ui' +import { InsertionSuggestion, InsertionSuggestionViewProps } from './InsertionSuggestion' const insertTimestamp = ({ editor, diff --git a/frontend/src/scenes/notebooks/Suggestions/SlashCommands.tsx b/frontend/src/scenes/notebooks/Suggestions/SlashCommands.tsx index 707bb1e46b90f..3c5c0710f48ee 100644 --- a/frontend/src/scenes/notebooks/Suggestions/SlashCommands.tsx +++ b/frontend/src/scenes/notebooks/Suggestions/SlashCommands.tsx @@ -1,9 +1,10 @@ -import { IconPlus } from 'lib/lemon-ui/icons' -import { InsertionSuggestion, InsertionSuggestionViewProps } from './InsertionSuggestion' -import { SlashCommandsPopover } from '../Notebook/SlashCommands' import { LemonButton } from '@posthog/lemon-ui' +import { IconPlus } from 'lib/lemon-ui/icons' import { useState } from 'react' +import { SlashCommandsPopover } from '../Notebook/SlashCommands' +import { InsertionSuggestion, InsertionSuggestionViewProps } from './InsertionSuggestion' + const Component = ({ editor }: InsertionSuggestionViewProps): JSX.Element => { const [visible, setVisible] = useState(false) diff --git a/frontend/src/scenes/notebooks/Suggestions/insertionSuggestionsLogic.ts b/frontend/src/scenes/notebooks/Suggestions/insertionSuggestionsLogic.ts index d7930368c4e18..8da79c7d6876c 100644 --- a/frontend/src/scenes/notebooks/Suggestions/insertionSuggestionsLogic.ts +++ b/frontend/src/scenes/notebooks/Suggestions/insertionSuggestionsLogic.ts @@ -1,9 +1,10 @@ import { actions, events, kea, listeners, path, reducers, selectors } from 'kea' + +import { Node, NotebookEditor } from '../Notebook/utils' +import { InsertionSuggestion } from './InsertionSuggestion' import type { insertionSuggestionsLogicType } from './insertionSuggestionsLogicType' import ReplayTimestampSuggestion from './ReplayTimestamp' import SlashCommands from './SlashCommands' -import { InsertionSuggestion } from './InsertionSuggestion' -import { Node, NotebookEditor } from '../Notebook/utils' export const insertionSuggestionsLogic = kea([ path(['scenes', 'notebooks', 'Suggestions', 'insertionSuggestionsLogic']), diff --git a/frontend/src/scenes/notebooks/notebookSceneLogic.ts b/frontend/src/scenes/notebooks/notebookSceneLogic.ts index 073dabbabe899..aa9c7fa0eabb3 100644 --- a/frontend/src/scenes/notebooks/notebookSceneLogic.ts +++ b/frontend/src/scenes/notebooks/notebookSceneLogic.ts @@ -1,11 +1,12 @@ import { afterMount, connect, kea, key, path, props, selectors } from 'kea' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { notebooksModel } from '~/models/notebooksModel' import { Breadcrumb, NotebookTarget } from '~/types' -import type { notebookSceneLogicType } from './notebookSceneLogicType' import { notebookLogic } from './Notebook/notebookLogic' -import { urls } from 'scenes/urls' -import { notebooksModel } from '~/models/notebooksModel' -import { Scene } from 'scenes/sceneTypes' +import type { notebookSceneLogicType } from './notebookSceneLogicType' export type NotebookSceneLogicProps = { shortId: string diff --git a/frontend/src/scenes/onboarding/Onboarding.tsx b/frontend/src/scenes/onboarding/Onboarding.tsx index 96badaa09ce95..f7e01007b81cf 100644 --- a/frontend/src/scenes/onboarding/Onboarding.tsx +++ b/frontend/src/scenes/onboarding/Onboarding.tsx @@ -1,15 +1,17 @@ -import { SceneExport } from 'scenes/sceneTypes' import { useActions, useValues } from 'kea' import { useEffect, useState } from 'react' -import { OnboardingStepKey, onboardingLogic } from './onboardingLogic' -import { SDKs } from './sdks/SDKs' +import { SceneExport } from 'scenes/sceneTypes' + import { ProductKey } from '~/types' -import { ProductAnalyticsSDKInstructions } from './sdks/product-analytics/ProductAnalyticsSDKInstructions' -import { SessionReplaySDKInstructions } from './sdks/session-replay/SessionReplaySDKInstructions' + import { OnboardingBillingStep } from './OnboardingBillingStep' +import { onboardingLogic, OnboardingStepKey } from './onboardingLogic' import { OnboardingOtherProductsStep } from './OnboardingOtherProductsStep' import { OnboardingVerificationStep } from './OnboardingVerificationStep' import { FeatureFlagsSDKInstructions } from './sdks/feature-flags/FeatureFlagsSDKInstructions' +import { ProductAnalyticsSDKInstructions } from './sdks/product-analytics/ProductAnalyticsSDKInstructions' +import { SDKs } from './sdks/SDKs' +import { SessionReplaySDKInstructions } from './sdks/session-replay/SessionReplaySDKInstructions' import { SurveysSDKInstructions } from './sdks/surveys/SurveysSDKInstructions' export const scene: SceneExport = { diff --git a/frontend/src/scenes/onboarding/OnboardingBillingStep.tsx b/frontend/src/scenes/onboarding/OnboardingBillingStep.tsx index 70e7852a70e44..18b3c77554ae2 100644 --- a/frontend/src/scenes/onboarding/OnboardingBillingStep.tsx +++ b/frontend/src/scenes/onboarding/OnboardingBillingStep.tsx @@ -1,17 +1,19 @@ -import { OnboardingStep } from './OnboardingStep' -import { PlanComparison } from 'scenes/billing/PlanComparison' +import { LemonBanner, LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { billingLogic } from 'scenes/billing/billingLogic' -import { OnboardingStepKey, onboardingLogic } from './onboardingLogic' -import { BillingProductV2Type } from '~/types' +import { StarHog } from 'lib/components/hedgehogs' +import { IconCheckCircleOutline } from 'lib/lemon-ui/icons' import { Spinner } from 'lib/lemon-ui/Spinner' -import { BillingHero } from 'scenes/billing/BillingHero' -import { LemonBanner, LemonButton } from '@posthog/lemon-ui' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { getUpgradeProductLink } from 'scenes/billing/billing-utils' +import { BillingHero } from 'scenes/billing/BillingHero' +import { billingLogic } from 'scenes/billing/billingLogic' import { billingProductLogic } from 'scenes/billing/billingProductLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { IconCheckCircleOutline } from 'lib/lemon-ui/icons' -import { StarHog } from 'lib/components/hedgehogs' +import { PlanComparison } from 'scenes/billing/PlanComparison' + +import { BillingProductV2Type } from '~/types' + +import { onboardingLogic, OnboardingStepKey } from './onboardingLogic' +import { OnboardingStep } from './OnboardingStep' export const OnboardingBillingStep = ({ product, diff --git a/frontend/src/scenes/onboarding/OnboardingOtherProductsStep.tsx b/frontend/src/scenes/onboarding/OnboardingOtherProductsStep.tsx index 6c3e92e9af68c..caac1d4f6cc0a 100644 --- a/frontend/src/scenes/onboarding/OnboardingOtherProductsStep.tsx +++ b/frontend/src/scenes/onboarding/OnboardingOtherProductsStep.tsx @@ -1,9 +1,10 @@ -import { useWindowSize } from 'lib/hooks/useWindowSize' -import { OnboardingStep } from './OnboardingStep' -import { OnboardingStepKey, onboardingLogic } from './onboardingLogic' import { useActions, useValues } from 'kea' +import { useWindowSize } from 'lib/hooks/useWindowSize' import { ProductCard } from 'scenes/products/Products' +import { onboardingLogic, OnboardingStepKey } from './onboardingLogic' +import { OnboardingStep } from './OnboardingStep' + export const OnboardingOtherProductsStep = ({ stepKey = OnboardingStepKey.OTHER_PRODUCTS, }: { diff --git a/frontend/src/scenes/onboarding/OnboardingStep.tsx b/frontend/src/scenes/onboarding/OnboardingStep.tsx index 171c607bf3d43..fde8a4dfff949 100644 --- a/frontend/src/scenes/onboarding/OnboardingStep.tsx +++ b/frontend/src/scenes/onboarding/OnboardingStep.tsx @@ -1,11 +1,12 @@ import { LemonButton } from '@posthog/lemon-ui' -import { BridgePage } from 'lib/components/BridgePage/BridgePage' -import { OnboardingStepKey, onboardingLogic } from './onboardingLogic' import { useActions, useValues } from 'kea' -import { IconArrowLeft, IconArrowRight } from 'lib/lemon-ui/icons' import { router } from 'kea-router' +import { BridgePage } from 'lib/components/BridgePage/BridgePage' +import { IconArrowLeft, IconArrowRight } from 'lib/lemon-ui/icons' import { urls } from 'scenes/urls' +import { onboardingLogic, OnboardingStepKey } from './onboardingLogic' + export const OnboardingStep = ({ stepKey, title, diff --git a/frontend/src/scenes/onboarding/OnboardingVerificationStep.tsx b/frontend/src/scenes/onboarding/OnboardingVerificationStep.tsx index 11ba1dc4fd065..358058a312e92 100644 --- a/frontend/src/scenes/onboarding/OnboardingVerificationStep.tsx +++ b/frontend/src/scenes/onboarding/OnboardingVerificationStep.tsx @@ -1,12 +1,13 @@ import { Spinner } from '@posthog/lemon-ui' -import { OnboardingStep } from './OnboardingStep' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { useInterval } from 'lib/hooks/useInterval' import { BlushingHog } from 'lib/components/hedgehogs' +import { useInterval } from 'lib/hooks/useInterval' import { capitalizeFirstLetter } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { teamLogic } from 'scenes/teamLogic' + import { OnboardingStepKey } from './onboardingLogic' +import { OnboardingStep } from './OnboardingStep' export const OnboardingVerificationStep = ({ listeningForName, diff --git a/frontend/src/scenes/onboarding/onboardingLogic.tsx b/frontend/src/scenes/onboarding/onboardingLogic.tsx index c5c02e1db0c60..f79b06d1a9011 100644 --- a/frontend/src/scenes/onboarding/onboardingLogic.tsx +++ b/frontend/src/scenes/onboarding/onboardingLogic.tsx @@ -1,11 +1,11 @@ -import { kea, props, path, connect, actions, reducers, selectors, listeners } from 'kea' -import { BillingProductV2Type, ProductKey } from '~/types' -import { urls } from 'scenes/urls' - +import { actions, connect, kea, listeners, path, props, reducers, selectors } from 'kea' +import { actionToUrl, combineUrl, router, urlToAction } from 'kea-router' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { billingLogic } from 'scenes/billing/billingLogic' import { teamLogic } from 'scenes/teamLogic' -import { combineUrl, router, actionToUrl, urlToAction } from 'kea-router' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { urls } from 'scenes/urls' + +import { BillingProductV2Type, ProductKey } from '~/types' import type { onboardingLogicType } from './onboardingLogicType' diff --git a/frontend/src/scenes/onboarding/sdks/SDKSnippet.tsx b/frontend/src/scenes/onboarding/sdks/SDKSnippet.tsx index d83a72c3d7eb1..1a3a90c87a3eb 100644 --- a/frontend/src/scenes/onboarding/sdks/SDKSnippet.tsx +++ b/frontend/src/scenes/onboarding/sdks/SDKSnippet.tsx @@ -1,6 +1,7 @@ -import { SDK } from '~/types' import { Link } from 'lib/lemon-ui/Link' +import { SDK } from '~/types' + export const SDKSnippet = ({ sdk, sdkInstructions }: { sdk: SDK; sdkInstructions: () => JSX.Element }): JSX.Element => { return (

    diff --git a/frontend/src/scenes/onboarding/sdks/SDKs.tsx b/frontend/src/scenes/onboarding/sdks/SDKs.tsx index 94556254fab58..3c51ba77b0a72 100644 --- a/frontend/src/scenes/onboarding/sdks/SDKs.tsx +++ b/frontend/src/scenes/onboarding/sdks/SDKs.tsx @@ -1,15 +1,17 @@ +import { IconArrowLeft } from '@posthog/icons' import { LemonButton, LemonCard, LemonDivider, LemonSelect } from '@posthog/lemon-ui' -import { sdksLogic } from './sdksLogic' import { useActions, useValues } from 'kea' -import { OnboardingStep } from '../OnboardingStep' -import { SDKSnippet } from './SDKSnippet' -import { OnboardingStepKey, onboardingLogic } from '../onboardingLogic' +import { useWindowSize } from 'lib/hooks/useWindowSize' import { useEffect } from 'react' import React from 'react' -import { SDKInstructionsMap } from '~/types' + import { InviteMembersButton } from '~/layout/navigation/TopBar/SitePopover' -import { IconArrowLeft } from '@posthog/icons' -import { useWindowSize } from 'lib/hooks/useWindowSize' +import { SDKInstructionsMap } from '~/types' + +import { onboardingLogic, OnboardingStepKey } from '../onboardingLogic' +import { OnboardingStep } from '../OnboardingStep' +import { sdksLogic } from './sdksLogic' +import { SDKSnippet } from './SDKSnippet' export function SDKs({ usersAction, diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/FeatureFlagsSDKInstructions.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/FeatureFlagsSDKInstructions.tsx index 729a335fa3004..fee11b72005d0 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/FeatureFlagsSDKInstructions.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/FeatureFlagsSDKInstructions.tsx @@ -1,17 +1,18 @@ import { SDKInstructionsMap, SDKKey } from '~/types' + import { - FeatureFlagsJSWebInstructions, - FeatureFlagsNextJSInstructions, - FeatureFlagsAPIInstructions, FeatureFlagsAndroidInstructions, + FeatureFlagsAPIInstructions, FeatureFlagsGoInstructions, FeatureFlagsIOSInstructions, + FeatureFlagsJSWebInstructions, + FeatureFlagsNextJSInstructions, FeatureFlagsNodeInstructions, FeatureFlagsPHPInstructions, FeatureFlagsPythonInstructions, + FeatureFlagsReactInstructions, FeatureFlagsRNInstructions, FeatureFlagsRubyInstructions, - FeatureFlagsReactInstructions, } from '.' export const FeatureFlagsSDKInstructions: SDKInstructionsMap = { diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/android.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/android.tsx index 0c9a64d274a8d..be4eda78a2056 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/android.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/android.tsx @@ -1,6 +1,7 @@ -import { FlagImplementationSnippet } from './flagImplementationSnippet' import { SDKKey } from '~/types' + import { SDKInstallAndroidInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsAndroidInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/api.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/api.tsx index 5402e66f53b48..2555cbcf8bb9e 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/api.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/api.tsx @@ -1,4 +1,5 @@ import { SDKKey } from '~/types' + import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsAPIInstructions(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/flagImplementationSnippet.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/flagImplementationSnippet.tsx index 38a4e9b81d546..a4c3abd3db803 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/flagImplementationSnippet.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/flagImplementationSnippet.tsx @@ -1,5 +1,6 @@ import { OPTIONS } from 'scenes/feature-flags/FeatureFlagCodeOptions' import { CodeInstructions } from 'scenes/feature-flags/FeatureFlagInstructions' + import { SDKKey } from '~/types' export const FlagImplementationSnippet = ({ sdkKey }: { sdkKey: SDKKey }): JSX.Element => { diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/go.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/go.tsx index cdb750a2396f8..f95758e28b13e 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/go.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/go.tsx @@ -1,6 +1,7 @@ import { SDKKey } from '~/types' -import { FlagImplementationSnippet } from './flagImplementationSnippet' + import { SDKInstallGoInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsGoInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/index.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/index.tsx index 11e1743082019..69d5234c62f1e 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/index.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/index.tsx @@ -1,12 +1,12 @@ export * from './android' +export * from './api' export * from './go' -export * from './nodejs' export * from './ios' +export * from './js-web' +export * from './next-js' +export * from './nodejs' export * from './php' export * from './python' +export * from './react' export * from './react-native' export * from './ruby' -export * from './api' -export * from './js-web' -export * from './react' -export * from './next-js' diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/ios.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/ios.tsx index 250c98fd4d3fd..5a9bee6e12017 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/ios.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/ios.tsx @@ -1,6 +1,7 @@ -import { FlagImplementationSnippet } from './flagImplementationSnippet' import { SDKKey } from '~/types' + import { SDKInstallIOSInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsIOSInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/js-web.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/js-web.tsx index 78a2fa373faa6..7479715a80442 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/js-web.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/js-web.tsx @@ -1,7 +1,9 @@ import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { FlagImplementationSnippet } from './flagImplementationSnippet' + import { SDKKey } from '~/types' + import { SDKInstallJSWebInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsJSWebInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/next-js.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/next-js.tsx index 7b1b37f16b2a1..121bb3aaea799 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/next-js.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/next-js.tsx @@ -1,7 +1,8 @@ import { SDKKey } from '~/types' -import { FlagImplementationSnippet } from './flagImplementationSnippet' -import { SDKInstallNextJSInstructions } from '../sdk-install-instructions/next-js' + import { NodeInstallSnippet, NodeSetupSnippet } from '../sdk-install-instructions' +import { SDKInstallNextJSInstructions } from '../sdk-install-instructions/next-js' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsNextJSInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/nodejs.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/nodejs.tsx index 576e6cd9091d2..9117e4b364683 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/nodejs.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/nodejs.tsx @@ -1,6 +1,7 @@ -import { FlagImplementationSnippet } from './flagImplementationSnippet' import { SDKKey } from '~/types' + import { SDKInstallNodeInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsNodeInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/php.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/php.tsx index 68a97ef96d9c4..6356354a84956 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/php.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/php.tsx @@ -1,6 +1,7 @@ import { SDKKey } from '~/types' -import { FlagImplementationSnippet } from './flagImplementationSnippet' + import { SDKInstallPHPInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsPHPInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/python.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/python.tsx index 55962b40f52ee..f06a7329e60f6 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/python.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/python.tsx @@ -1,6 +1,7 @@ import { SDKKey } from '~/types' -import { FlagImplementationSnippet } from './flagImplementationSnippet' + import { SDKInstallPythonInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsPythonInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/react-native.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/react-native.tsx index f045c817abcb8..f542713bea88e 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/react-native.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/react-native.tsx @@ -1,6 +1,7 @@ +import { SDKKey } from '~/types' + import { SDKInstallRNInstructions } from '../sdk-install-instructions' import { FlagImplementationSnippet } from './flagImplementationSnippet' -import { SDKKey } from '~/types' export function FeatureFlagsRNInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/react.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/react.tsx index 35ff77019b763..45803d51941f0 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/react.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/react.tsx @@ -1,6 +1,7 @@ -import { FlagImplementationSnippet } from './flagImplementationSnippet' import { SDKKey } from '~/types' + import { SDKInstallReactInstructions } from '../sdk-install-instructions/react' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsReactInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/feature-flags/ruby.tsx b/frontend/src/scenes/onboarding/sdks/feature-flags/ruby.tsx index 388d934ede926..fd84d557b7ba3 100644 --- a/frontend/src/scenes/onboarding/sdks/feature-flags/ruby.tsx +++ b/frontend/src/scenes/onboarding/sdks/feature-flags/ruby.tsx @@ -1,6 +1,7 @@ -import { FlagImplementationSnippet } from './flagImplementationSnippet' import { SDKKey } from '~/types' + import { SDKInstallRubyInstructions } from '../sdk-install-instructions' +import { FlagImplementationSnippet } from './flagImplementationSnippet' export function FeatureFlagsRubyInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/ProductAnalyticsSDKInstructions.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/ProductAnalyticsSDKInstructions.tsx index f572132017ab4..181aa04218e40 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/ProductAnalyticsSDKInstructions.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/ProductAnalyticsSDKInstructions.tsx @@ -1,8 +1,9 @@ import { SDKInstructionsMap, SDKKey } from '~/types' + import { JSWebInstructions, - ProductAnalyticsAPIInstructions, ProductAnalyticsAndroidInstructions, + ProductAnalyticsAPIInstructions, ProductAnalyticsElixirInstructions, ProductAnalyticsFlutterInstructions, ProductAnalyticsGoInstructions, diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/android.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/android.tsx index 71435dd4fdee1..3bdbc959aed8d 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/android.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/android.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallAndroidInstructions } from '../sdk-install-instructions' function AndroidCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/api.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/api.tsx index 64974e21d1905..263d14511865d 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/api.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/api.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function APISnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/flutter.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/flutter.tsx index 01c793bfc8d74..a5cd98e80bf96 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/flutter.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/flutter.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallFlutterInstructions } from '../sdk-install-instructions' function FlutterCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/go.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/go.tsx index 7d7d14f0cd818..b674d6567253a 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/go.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/go.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallGoInstructions } from '../sdk-install-instructions' function GoCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/index.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/index.tsx index 0680cf876820f..dc6b1dd3e0bf8 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/index.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/index.tsx @@ -1,12 +1,12 @@ export * from './android' +export * from './api' +export * from './elixir' +export * from './flutter' export * from './go' -export * from './nodejs' export * from './ios' +export * from './js-web' +export * from './nodejs' export * from './php' export * from './python' export * from './react-native' export * from './ruby' -export * from './api' -export * from './elixir' -export * from './flutter' -export * from './js-web' diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/ios.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/ios.tsx index 79ae931729710..b2ff488df0719 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/ios.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/ios.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallIOSInstructions } from '../sdk-install-instructions' function IOS_OBJ_C_CaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/js-web.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/js-web.tsx index fc2eb0f53c67d..5491625dd047e 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/js-web.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/js-web.tsx @@ -1,6 +1,7 @@ +import { LemonDivider } from '@posthog/lemon-ui' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallJSWebInstructions } from '../sdk-install-instructions' -import { LemonDivider } from '@posthog/lemon-ui' function JSEventSnippet(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/nodejs.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/nodejs.tsx index 6a6050ca44f49..f679751e5571e 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/nodejs.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/nodejs.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallNodeInstructions } from '../sdk-install-instructions' function NodeCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/php.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/php.tsx index 2704a4c285e2b..ba8f6387cef2a 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/php.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/php.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallPHPInstructions } from '../sdk-install-instructions' function PHPCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/python.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/python.tsx index 486326bf34669..fd49bb32b86b3 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/python.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/python.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallPythonInstructions } from '../sdk-install-instructions' function PythonCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/react-native.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/react-native.tsx index 0492b8c210960..459644b3ec02a 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/react-native.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/react-native.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallRNInstructions } from '../sdk-install-instructions' export function ProductAnalyticsRNInstructions(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/product-analytics/ruby.tsx b/frontend/src/scenes/onboarding/sdks/product-analytics/ruby.tsx index 905897614ebcd..a02e140db827e 100644 --- a/frontend/src/scenes/onboarding/sdks/product-analytics/ruby.tsx +++ b/frontend/src/scenes/onboarding/sdks/product-analytics/ruby.tsx @@ -1,4 +1,5 @@ import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { SDKInstallRubyInstructions } from '../sdk-install-instructions' function RubyCaptureSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/android.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/android.tsx index 01a4b7d11d934..b29914de6d192 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/android.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/android.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function AndroidInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/elixir.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/elixir.tsx index 2378c5ef93d0b..c73c631373462 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/elixir.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/elixir.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function ElixirInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/flutter.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/flutter.tsx index e37b2b1038388..b2284e00d6596 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/flutter.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/flutter.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function FlutterInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/go.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/go.tsx index 87bf25b337c40..d29559e6067af 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/go.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/go.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function GoInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/index.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/index.tsx index cc0382dd22581..0765b5bbf12f6 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/index.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/index.tsx @@ -1,11 +1,11 @@ export * from './android' +export * from './elixir' +export * from './flutter' export * from './go' -export * from './nodejs' export * from './ios' +export * from './js-web' +export * from './nodejs' export * from './php' export * from './python' export * from './react-native' export * from './ruby' -export * from './elixir' -export * from './flutter' -export * from './js-web' diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ios.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ios.tsx index 314f4c0305343..ce68fc3a5225a 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ios.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ios.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function IOSInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/js-web.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/js-web.tsx index 88b5e8acc8adc..f560a0f20d3a5 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/js-web.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/js-web.tsx @@ -1,7 +1,7 @@ +import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { JSSnippet } from 'lib/components/JSSnippet' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { useValues } from 'kea' import { teamLogic } from 'scenes/teamLogic' export function JSInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/next-js.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/next-js.tsx index e3312f1e07dd8..f8f2c17e0be8d 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/next-js.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/next-js.tsx @@ -1,7 +1,8 @@ -import { Link } from 'lib/lemon-ui/Link' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' +import { Link } from 'lib/lemon-ui/Link' import { teamLogic } from 'scenes/teamLogic' + import { JSInstallSnippet } from './js-web' function NextEnvVarsSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/nodejs.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/nodejs.tsx index bab12bd12c45e..794cbbec86daa 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/nodejs.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/nodejs.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' export function NodeInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/php.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/php.tsx index 136dee636404a..6b7aa26973ec1 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/php.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/php.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function PHPConfigSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/python.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/python.tsx index 54ece50952ec3..1f58740d96192 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/python.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/python.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function PythonInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react-native.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react-native.tsx index 298cb434f6751..6b489e0088ad2 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react-native.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react-native.tsx @@ -1,7 +1,7 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' +import { Link } from '@posthog/lemon-ui' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' -import { Link } from '@posthog/lemon-ui' export function SDKInstallRNInstructions(): JSX.Element { const { currentTeam } = useValues(teamLogic) diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react.tsx index a066848419416..8941ac2f45c24 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/react.tsx @@ -1,6 +1,7 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' + import { JSInstallSnippet } from './js-web' function ReactEnvVarsSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ruby.tsx b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ruby.tsx index bd5521f351983..d51f97cd8c9e7 100644 --- a/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ruby.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdk-install-instructions/ruby.tsx @@ -1,5 +1,5 @@ -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { useValues } from 'kea' +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { teamLogic } from 'scenes/teamLogic' function RubyInstallSnippet(): JSX.Element { diff --git a/frontend/src/scenes/onboarding/sdks/sdksLogic.tsx b/frontend/src/scenes/onboarding/sdks/sdksLogic.tsx index 2cfef0711bf10..26c75e8d4d5f4 100644 --- a/frontend/src/scenes/onboarding/sdks/sdksLogic.tsx +++ b/frontend/src/scenes/onboarding/sdks/sdksLogic.tsx @@ -1,10 +1,11 @@ -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' +import { LemonSelectOptions } from 'lib/lemon-ui/LemonSelect/LemonSelect' -import type { sdksLogicType } from './sdksLogicType' import { SDK, SDKInstructionsMap } from '~/types' + import { onboardingLogic } from '../onboardingLogic' import { allSDKs } from './allSDKs' -import { LemonSelectOptions } from 'lib/lemon-ui/LemonSelect/LemonSelect' +import type { sdksLogicType } from './sdksLogicType' /* To add SDK instructions for your product: diff --git a/frontend/src/scenes/onboarding/sdks/session-replay/SessionReplaySDKInstructions.tsx b/frontend/src/scenes/onboarding/sdks/session-replay/SessionReplaySDKInstructions.tsx index 81d8068d956ca..bf8566b5dcc77 100644 --- a/frontend/src/scenes/onboarding/sdks/session-replay/SessionReplaySDKInstructions.tsx +++ b/frontend/src/scenes/onboarding/sdks/session-replay/SessionReplaySDKInstructions.tsx @@ -1,4 +1,5 @@ import { SDKInstructionsMap, SDKKey } from '~/types' + import { JSWebInstructions, NextJSInstructions, ReactInstructions } from '.' export const SessionReplaySDKInstructions: SDKInstructionsMap = { diff --git a/frontend/src/scenes/onboarding/sdks/session-replay/js-web.tsx b/frontend/src/scenes/onboarding/sdks/session-replay/js-web.tsx index fc799bbd8a65a..d465faf1de9cb 100644 --- a/frontend/src/scenes/onboarding/sdks/session-replay/js-web.tsx +++ b/frontend/src/scenes/onboarding/sdks/session-replay/js-web.tsx @@ -1,6 +1,7 @@ import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { SessionReplayFinalSteps } from '../shared-snippets' + import { SDKInstallJSWebInstructions } from '../sdk-install-instructions' +import { SessionReplayFinalSteps } from '../shared-snippets' export function JSWebInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/session-replay/next-js.tsx b/frontend/src/scenes/onboarding/sdks/session-replay/next-js.tsx index dadd37388cb0a..8173f3be97115 100644 --- a/frontend/src/scenes/onboarding/sdks/session-replay/next-js.tsx +++ b/frontend/src/scenes/onboarding/sdks/session-replay/next-js.tsx @@ -1,5 +1,5 @@ -import { SessionReplayFinalSteps } from '../shared-snippets' import { SDKInstallNextJSInstructions } from '../sdk-install-instructions/next-js' +import { SessionReplayFinalSteps } from '../shared-snippets' export function NextJSInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/session-replay/react.tsx b/frontend/src/scenes/onboarding/sdks/session-replay/react.tsx index 361884112a15b..f9863c945dd75 100644 --- a/frontend/src/scenes/onboarding/sdks/session-replay/react.tsx +++ b/frontend/src/scenes/onboarding/sdks/session-replay/react.tsx @@ -1,5 +1,5 @@ -import { SessionReplayFinalSteps } from '../shared-snippets' import { SDKInstallReactInstructions } from '../sdk-install-instructions/react' +import { SessionReplayFinalSteps } from '../shared-snippets' export function ReactInstructions(): JSX.Element { return ( diff --git a/frontend/src/scenes/onboarding/sdks/surveys/SurveysSDKInstructions.tsx b/frontend/src/scenes/onboarding/sdks/surveys/SurveysSDKInstructions.tsx index 352a4b3d96d82..ad9db67494c88 100644 --- a/frontend/src/scenes/onboarding/sdks/surveys/SurveysSDKInstructions.tsx +++ b/frontend/src/scenes/onboarding/sdks/surveys/SurveysSDKInstructions.tsx @@ -1,4 +1,5 @@ import { SDKInstructionsMap, SDKKey } from '~/types' + import { JSWebInstructions, NextJSInstructions, ReactInstructions } from '.' export const SurveysSDKInstructions: SDKInstructionsMap = { diff --git a/frontend/src/scenes/onboarding/sdks/surveys/js-web.tsx b/frontend/src/scenes/onboarding/sdks/surveys/js-web.tsx index 522c229904cc7..d478de9b3dd0b 100644 --- a/frontend/src/scenes/onboarding/sdks/surveys/js-web.tsx +++ b/frontend/src/scenes/onboarding/sdks/surveys/js-web.tsx @@ -1,4 +1,5 @@ import { LemonDivider } from 'lib/lemon-ui/LemonDivider' + import { SDKInstallJSWebInstructions } from '../sdk-install-instructions' import { SurveysFinalSteps } from './SurveysFinalSteps' diff --git a/frontend/src/scenes/organization/ConfirmOrganization/ConfirmOrganization.tsx b/frontend/src/scenes/organization/ConfirmOrganization/ConfirmOrganization.tsx index 4a151a7d21b94..e62ccf55c058e 100644 --- a/frontend/src/scenes/organization/ConfirmOrganization/ConfirmOrganization.tsx +++ b/frontend/src/scenes/organization/ConfirmOrganization/ConfirmOrganization.tsx @@ -1,17 +1,18 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { organizationLogic } from 'scenes/organizationLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { confirmOrganizationLogic } from './confirmOrganizationLogic' -import { Field } from 'lib/forms/Field' -import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' import { Form } from 'kea-forms' +import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' import { BridgePage } from 'lib/components/BridgePage/BridgePage' -import SignupRoleSelect from 'lib/components/SignupRoleSelect' import SignupReferralSource from 'lib/components/SignupReferralSource' -import { Link } from '@posthog/lemon-ui' +import SignupRoleSelect from 'lib/components/SignupRoleSelect' +import { Field } from 'lib/forms/Field' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { organizationLogic } from 'scenes/organizationLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { confirmOrganizationLogic } from './confirmOrganizationLogic' export const scene: SceneExport = { component: ConfirmOrganization, diff --git a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.test.ts b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.test.ts index 13c651dca5aa6..599f90de5ff89 100644 --- a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.test.ts +++ b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.test.ts @@ -1,7 +1,9 @@ -import { confirmOrganizationLogic } from './confirmOrganizationLogic' +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' + import { initKeaTests } from '~/test/init' -import { router } from 'kea-router' + +import { confirmOrganizationLogic } from './confirmOrganizationLogic' describe('confirmOrganizationLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts index c0c9126454968..84e328f82a51a 100644 --- a/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts +++ b/frontend/src/scenes/organization/ConfirmOrganization/confirmOrganizationLogic.ts @@ -1,11 +1,10 @@ import { actions, kea, path, reducers } from 'kea' - -import api from 'lib/api' +import { forms } from 'kea-forms' import { urlToAction } from 'kea-router' +import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import type { confirmOrganizationLogicType } from './confirmOrganizationLogicType' -import { forms } from 'kea-forms' -import { lemonToast } from 'lib/lemon-ui/lemonToast' export interface ConfirmOrganizationFormValues { organization_name?: string diff --git a/frontend/src/scenes/organization/Create/index.tsx b/frontend/src/scenes/organization/Create/index.tsx index 2c9d5d1aaa710..32c6c03580584 100644 --- a/frontend/src/scenes/organization/Create/index.tsx +++ b/frontend/src/scenes/organization/Create/index.tsx @@ -1,6 +1,7 @@ -import { CreateOrganizationModal } from '../CreateOrganizationModal' -import { SceneExport } from 'scenes/sceneTypes' import { organizationLogic } from 'scenes/organizationLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { CreateOrganizationModal } from '../CreateOrganizationModal' export const scene: SceneExport = { component: OrganizationCreate, diff --git a/frontend/src/scenes/organization/membersLogic.tsx b/frontend/src/scenes/organization/membersLogic.tsx index ebf874aaa5063..49a5ac406bc87 100644 --- a/frontend/src/scenes/organization/membersLogic.tsx +++ b/frontend/src/scenes/organization/membersLogic.tsx @@ -1,14 +1,16 @@ +import Fuse from 'fuse.js' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' import api from 'lib/api' -import type { membersLogicType } from './membersLogicType' import { OrganizationMembershipLevel } from 'lib/constants' -import { OrganizationMemberType } from '~/types' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { membershipLevelToName } from 'lib/utils/permissioning' import { organizationLogic } from 'scenes/organizationLogic' import { userLogic } from 'scenes/userLogic' -import { membershipLevelToName } from 'lib/utils/permissioning' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import Fuse from 'fuse.js' + +import { OrganizationMemberType } from '~/types' + +import type { membersLogicType } from './membersLogicType' export interface MembersFuse extends Fuse {} diff --git a/frontend/src/scenes/organizationLogic.test.ts b/frontend/src/scenes/organizationLogic.test.ts index 464a89b39f763..496f6f7307740 100644 --- a/frontend/src/scenes/organizationLogic.test.ts +++ b/frontend/src/scenes/organizationLogic.test.ts @@ -1,6 +1,8 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' import { MOCK_DEFAULT_ORGANIZATION } from 'lib/api.mock' + +import { initKeaTests } from '~/test/init' + import { AppContext } from '../types' import { organizationLogic } from './organizationLogic' diff --git a/frontend/src/scenes/organizationLogic.tsx b/frontend/src/scenes/organizationLogic.tsx index 873903881c6b9..544ddd623d90d 100644 --- a/frontend/src/scenes/organizationLogic.tsx +++ b/frontend/src/scenes/organizationLogic.tsx @@ -1,13 +1,15 @@ import { actions, afterMount, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import api, { ApiConfig } from 'lib/api' -import type { organizationLogicType } from './organizationLogicType' -import { AvailableFeature, OrganizationType } from '~/types' -import { userLogic } from './userLogic' -import { getAppContext } from 'lib/utils/getAppContext' import { OrganizationMembershipLevel } from 'lib/constants' -import { isUserLoggedIn } from 'lib/utils' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { loaders } from 'kea-loaders' +import { isUserLoggedIn } from 'lib/utils' +import { getAppContext } from 'lib/utils/getAppContext' + +import { AvailableFeature, OrganizationType } from '~/types' + +import type { organizationLogicType } from './organizationLogicType' +import { userLogic } from './userLogic' export type OrganizationUpdatePayload = Partial< Pick diff --git a/frontend/src/scenes/paths/PathNodeCard.tsx b/frontend/src/scenes/paths/PathNodeCard.tsx index 7c5a6a4f6e786..2f999f94bef03 100644 --- a/frontend/src/scenes/paths/PathNodeCard.tsx +++ b/frontend/src/scenes/paths/PathNodeCard.tsx @@ -1,14 +1,14 @@ -import { useActions, useValues } from 'kea' +import { Tooltip } from '@posthog/lemon-ui' import { Dropdown } from 'antd' +import { useActions, useValues } from 'kea' import { InsightLogicProps } from '~/types' -import { pageUrl, isSelectedPathStartOrEnd, PathNodeData } from './pathUtils' -import { PathNodeCardMenu } from './PathNodeCardMenu' -import { PathNodeCardButton } from './PathNodeCardButton' import { PATH_NODE_CARD_LEFT_OFFSET, PATH_NODE_CARD_TOP_OFFSET, PATH_NODE_CARD_WIDTH } from './constants' +import { PathNodeCardButton } from './PathNodeCardButton' +import { PathNodeCardMenu } from './PathNodeCardMenu' import { pathsDataLogic } from './pathsDataLogic' -import { Tooltip } from '@posthog/lemon-ui' +import { isSelectedPathStartOrEnd, pageUrl, PathNodeData } from './pathUtils' export type PathNodeCardProps = { insightProps: InsightLogicProps diff --git a/frontend/src/scenes/paths/PathNodeCardButton.tsx b/frontend/src/scenes/paths/PathNodeCardButton.tsx index 4e491d5501e81..b6f29e57739c7 100644 --- a/frontend/src/scenes/paths/PathNodeCardButton.tsx +++ b/frontend/src/scenes/paths/PathNodeCardButton.tsx @@ -1,15 +1,14 @@ +import { LemonButton, LemonButtonWithDropdown } from '@posthog/lemon-ui' +import { captureException } from '@sentry/react' import { useValues } from 'kea' - +import { IconEllipsis } from 'lib/lemon-ui/icons' +import { copyToClipboard } from 'lib/utils/copyToClipboard' import { userLogic } from 'scenes/userLogic' import { AvailableFeature, PathsFilterType } from '~/types' -import { LemonButton, LemonButtonWithDropdown } from '@posthog/lemon-ui' -import { IconEllipsis } from 'lib/lemon-ui/icons' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { pageUrl, PathNodeData } from './pathUtils' import { pathsDataLogicType } from './pathsDataLogicType' -import { captureException } from '@sentry/react' +import { pageUrl, PathNodeData } from './pathUtils' type PathNodeCardButton = { name: string diff --git a/frontend/src/scenes/paths/PathNodeCardMenu.tsx b/frontend/src/scenes/paths/PathNodeCardMenu.tsx index d860ec441cc8c..ec61abdd3d935 100644 --- a/frontend/src/scenes/paths/PathNodeCardMenu.tsx +++ b/frontend/src/scenes/paths/PathNodeCardMenu.tsx @@ -1,11 +1,10 @@ -import { MouseEventHandler } from 'react' - import { LemonButton } from '@posthog/lemon-ui' -import { IconTrendingFlat, IconTrendingFlatDown, IconSchedule } from 'lib/lemon-ui/icons' +import { IconSchedule, IconTrendingFlat, IconTrendingFlatDown } from 'lib/lemon-ui/icons' import { humanFriendlyDuration } from 'lib/utils' +import { MouseEventHandler } from 'react' -import { pathsDataLogicType } from './pathsDataLogicType' import { PATH_NODE_CARD_WIDTH } from './constants' +import { pathsDataLogicType } from './pathsDataLogicType' type PathNodeCardMenuProps = { name: string diff --git a/frontend/src/scenes/paths/Paths.tsx b/frontend/src/scenes/paths/Paths.tsx index 7ddebb7033fb0..e865923142e17 100644 --- a/frontend/src/scenes/paths/Paths.tsx +++ b/frontend/src/scenes/paths/Paths.tsx @@ -1,16 +1,15 @@ -import { useRef, useEffect, useState } from 'react' +import './Paths.scss' + import { useValues } from 'kea' import { useResizeObserver } from 'lib/hooks/useResizeObserver' - +import { useEffect, useRef, useState } from 'react' +import { InsightEmptyState, InsightErrorState } from 'scenes/insights/EmptyStates' import { insightLogic } from 'scenes/insights/insightLogic' -import { pathsDataLogic } from './pathsDataLogic' -import { InsightEmptyState, InsightErrorState } from 'scenes/insights/EmptyStates' import { PathNodeCard } from './PathNodeCard' -import { renderPaths } from './renderPaths' +import { pathsDataLogic } from './pathsDataLogic' import type { PathNodeData } from './pathUtils' - -import './Paths.scss' +import { renderPaths } from './renderPaths' const DEFAULT_PATHS_ID = 'default_paths' export const HIDE_PATH_CARD_HEIGHT = 30 diff --git a/frontend/src/scenes/paths/pathUtils.ts b/frontend/src/scenes/paths/pathUtils.ts index 4ca7ee1bd93b4..3458de87e6a03 100644 --- a/frontend/src/scenes/paths/pathUtils.ts +++ b/frontend/src/scenes/paths/pathUtils.ts @@ -1,4 +1,5 @@ import { RGBColor } from 'd3' + import { FunnelPathType, PathsFilterType } from '~/types' export interface PathTargetLink { diff --git a/frontend/src/scenes/paths/pathsDataLogic.test.ts b/frontend/src/scenes/paths/pathsDataLogic.test.ts index 85745f976ab62..15b7dac69bcef 100644 --- a/frontend/src/scenes/paths/pathsDataLogic.test.ts +++ b/frontend/src/scenes/paths/pathsDataLogic.test.ts @@ -1,10 +1,9 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' - +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { pathsDataLogic } from 'scenes/paths/pathsDataLogic' +import { initKeaTests } from '~/test/init' import { InsightLogicProps, InsightType, PathType } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' let logic: ReturnType diff --git a/frontend/src/scenes/paths/pathsDataLogic.ts b/frontend/src/scenes/paths/pathsDataLogic.ts index 7a9796901f2b7..22af56a4c585c 100644 --- a/frontend/src/scenes/paths/pathsDataLogic.ts +++ b/frontend/src/scenes/paths/pathsDataLogic.ts @@ -1,26 +1,27 @@ -import { kea, path, props, key, connect, selectors, actions, listeners } from 'kea' +import { actions, connect, kea, key, listeners, path, props, selectors } from 'kea' +import { router } from 'kea-router' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { buildPeopleUrl, pathsTitle } from 'scenes/trends/persons-modal/persons-modal-utils' +import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' +import { urls } from 'scenes/urls' + +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { InsightQueryNode } from '~/queries/schema' +import { isPathsQuery } from '~/queries/utils' import { + ActionFilter, InsightLogicProps, - PathType, - PathsFilterType, InsightType, - ActionFilter, - PropertyOperator, + PathsFilterType, + PathType, PropertyFilterType, + PropertyOperator, } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import type { pathsDataLogicType } from './pathsDataLogicType' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { isPathsQuery } from '~/queries/utils' import { PathNodeData } from './pathUtils' -import { buildPeopleUrl, pathsTitle } from 'scenes/trends/persons-modal/persons-modal-utils' -import { openPersonsModal } from 'scenes/trends/persons-modal/PersonsModal' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { InsightQueryNode } from '~/queries/schema' export const DEFAULT_STEP_LIMIT = 5 diff --git a/frontend/src/scenes/paths/renderPaths.ts b/frontend/src/scenes/paths/renderPaths.ts index 22244d11d1727..891ebd67665fa 100644 --- a/frontend/src/scenes/paths/renderPaths.ts +++ b/frontend/src/scenes/paths/renderPaths.ts @@ -1,14 +1,14 @@ -import { Dispatch, RefObject, SetStateAction } from 'react' import * as d3 from 'd3' import * as Sankey from 'd3-sankey' +import { D3Selector } from 'lib/hooks/useD3' +import { stripHTTP } from 'lib/utils' +import { Dispatch, RefObject, SetStateAction } from 'react' import { PathsFilterType } from '~/types' -import { stripHTTP } from 'lib/utils' -import { D3Selector } from 'lib/hooks/useD3' -import { HIDE_PATH_CARD_HEIGHT, FALLBACK_CANVAS_WIDTH } from './Paths' -import { roundedRect, isSelectedPathStartOrEnd, PathNodeData, PathTargetLink } from './pathUtils' +import { FALLBACK_CANVAS_WIDTH, HIDE_PATH_CARD_HEIGHT } from './Paths' import { PathNode } from './pathsDataLogic' +import { isSelectedPathStartOrEnd, PathNodeData, PathTargetLink, roundedRect } from './pathUtils' const createCanvas = (canvasRef: RefObject, width: number, height: number): D3Selector => { return d3 diff --git a/frontend/src/scenes/persons-management/PersonsManagementScene.tsx b/frontend/src/scenes/persons-management/PersonsManagementScene.tsx index 156f54d1d6342..87e1f318f7216 100644 --- a/frontend/src/scenes/persons-management/PersonsManagementScene.tsx +++ b/frontend/src/scenes/persons-management/PersonsManagementScene.tsx @@ -1,10 +1,11 @@ import { useActions, useValues } from 'kea' +import { PageHeader } from 'lib/components/PageHeader' import { LemonTab, LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { SceneExport } from 'scenes/sceneTypes' import { groupsModel } from '~/models/groupsModel' -import { PageHeader } from 'lib/components/PageHeader' + import { personsManagementSceneLogic } from './personsManagementSceneLogic' -import { SceneExport } from 'scenes/sceneTypes' export function PersonsManagementScene(): JSX.Element { const { tabs, activeTab, tabKey } = useValues(personsManagementSceneLogic) diff --git a/frontend/src/scenes/persons-management/personsManagementSceneLogic.tsx b/frontend/src/scenes/persons-management/personsManagementSceneLogic.tsx index c2e1fac031681..7734a37566197 100644 --- a/frontend/src/scenes/persons-management/personsManagementSceneLogic.tsx +++ b/frontend/src/scenes/persons-management/personsManagementSceneLogic.tsx @@ -1,19 +1,19 @@ +import { LemonButton } from '@posthog/lemon-ui' import { actions, connect, kea, path, reducers, selectors } from 'kea' import { actionToUrl, router, urlToAction } from 'kea-router' -import { urls } from 'scenes/urls' +import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' import { LemonTab } from 'lib/lemon-ui/LemonTabs' -import { Breadcrumb } from '~/types' import { capitalizeFirstLetter } from 'lib/utils' -import { GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' +import { Cohorts } from 'scenes/cohorts/Cohorts' +import { Groups } from 'scenes/groups/Groups' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { groupsModel } from '~/models/groupsModel' -import { Persons } from './tabs/Persons' -import { Cohorts } from 'scenes/cohorts/Cohorts' -import { LemonButton } from '@posthog/lemon-ui' +import { Breadcrumb } from '~/types' import type { personsManagementSceneLogicType } from './personsManagementSceneLogicType' -import { Groups } from 'scenes/groups/Groups' -import { Scene } from 'scenes/sceneTypes' +import { Persons } from './tabs/Persons' export type PersonsManagementTab = { key: string diff --git a/frontend/src/scenes/persons-management/tabs/Persons.tsx b/frontend/src/scenes/persons-management/tabs/Persons.tsx index 15fb70d8f518f..5adb633e66ffb 100644 --- a/frontend/src/scenes/persons-management/tabs/Persons.tsx +++ b/frontend/src/scenes/persons-management/tabs/Persons.tsx @@ -1,5 +1,6 @@ import { useActions, useValues } from 'kea' import { personsSceneLogic } from 'scenes/persons-management/tabs/personsSceneLogic' + import { Query } from '~/queries/Query/Query' export function Persons(): JSX.Element { diff --git a/frontend/src/scenes/persons-management/tabs/personsSceneLogic.ts b/frontend/src/scenes/persons-management/tabs/personsSceneLogic.ts index b1c5321c99253..a96c0291d2379 100644 --- a/frontend/src/scenes/persons-management/tabs/personsSceneLogic.ts +++ b/frontend/src/scenes/persons-management/tabs/personsSceneLogic.ts @@ -1,11 +1,11 @@ import { actions, connect, kea, path, reducers, selectors } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' import { DataTableNode, Node, NodeKind } from '~/queries/schema' import type { personsSceneLogicType } from './personsSceneLogicType' -import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' const getDefaultQuery = (usePersonsQuery = false): DataTableNode => ({ kind: NodeKind.DataTableNode, diff --git a/frontend/src/scenes/persons/GroupActorDisplay.tsx b/frontend/src/scenes/persons/GroupActorDisplay.tsx index a39c4b9bab7a9..a41d42b07f987 100644 --- a/frontend/src/scenes/persons/GroupActorDisplay.tsx +++ b/frontend/src/scenes/persons/GroupActorDisplay.tsx @@ -1,8 +1,10 @@ -import { GroupActorType } from '~/types' import './PersonDisplay.scss' + import { Link } from 'lib/lemon-ui/Link' import { urls } from 'scenes/urls' +import { GroupActorType } from '~/types' + export interface GroupActorDisplayProps { actor: GroupActorType } diff --git a/frontend/src/scenes/persons/MergeSplitPerson.tsx b/frontend/src/scenes/persons/MergeSplitPerson.tsx index 8f1796c0c56d6..ca9725ee9587f 100644 --- a/frontend/src/scenes/persons/MergeSplitPerson.tsx +++ b/frontend/src/scenes/persons/MergeSplitPerson.tsx @@ -1,10 +1,13 @@ -import { Select, Modal } from 'antd' -import { PersonType } from '~/types' -import { useActions, useValues, BindLogic } from 'kea' import './MergeSplitPerson.scss' -import { mergeSplitPersonLogic } from './mergeSplitPersonLogic' -import { pluralize } from 'lib/utils' + +import { Modal, Select } from 'antd' +import { BindLogic, useActions, useValues } from 'kea' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { pluralize } from 'lib/utils' + +import { PersonType } from '~/types' + +import { mergeSplitPersonLogic } from './mergeSplitPersonLogic' export function MergeSplitPerson({ person }: { person: PersonType }): JSX.Element { const logicProps = { person } diff --git a/frontend/src/scenes/persons/NewProperty.tsx b/frontend/src/scenes/persons/NewProperty.tsx index b0ddfda001016..3df2b183ff87c 100644 --- a/frontend/src/scenes/persons/NewProperty.tsx +++ b/frontend/src/scenes/persons/NewProperty.tsx @@ -1,6 +1,6 @@ -import { useState } from 'react' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonInput, LemonLabel, LemonModal, LemonSegmentedButton } from '@posthog/lemon-ui' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useState } from 'react' interface NewPropertyInterface { creating: boolean diff --git a/frontend/src/scenes/persons/PersonCohorts.tsx b/frontend/src/scenes/persons/PersonCohorts.tsx index 7ae2d11912d38..66ebfa04144f1 100644 --- a/frontend/src/scenes/persons/PersonCohorts.tsx +++ b/frontend/src/scenes/persons/PersonCohorts.tsx @@ -1,10 +1,12 @@ -import { useEffect } from 'react' import { useActions, useValues } from 'kea' -import { personsLogic } from './personsLogic' -import { CohortType } from '~/types' import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { urls } from 'scenes/urls' import { Link } from 'lib/lemon-ui/Link' +import { useEffect } from 'react' +import { urls } from 'scenes/urls' + +import { CohortType } from '~/types' + +import { personsLogic } from './personsLogic' export function PersonCohorts(): JSX.Element { const { cohorts, cohortsLoading, person } = useValues(personsLogic) diff --git a/frontend/src/scenes/persons/PersonDashboard.tsx b/frontend/src/scenes/persons/PersonDashboard.tsx index 877bd35efb3c3..9f58578487ba4 100644 --- a/frontend/src/scenes/persons/PersonDashboard.tsx +++ b/frontend/src/scenes/persons/PersonDashboard.tsx @@ -1,14 +1,15 @@ -import { HogQLPropertyFilter, PersonType, PropertyFilterType } from '~/types' -import { Scene } from 'scenes/sceneTypes' -import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' +import { useActions, useValues } from 'kea' import { SceneDashboardChoiceModal } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceModal' import { sceneDashboardChoiceModalLogic } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' -import { useActions, useValues } from 'kea' -import { Dashboard } from 'scenes/dashboard/Dashboard' -import { personDashboardLogic } from 'scenes/persons/personDashboardLogic' +import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { useEffect } from 'react' +import { Dashboard } from 'scenes/dashboard/Dashboard' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' +import { personDashboardLogic } from 'scenes/persons/personDashboardLogic' +import { Scene } from 'scenes/sceneTypes' + +import { HogQLPropertyFilter, PersonType, PropertyFilterType } from '~/types' export function PersonDashboard({ person }: { person: PersonType }): JSX.Element { const { showSceneDashboardChoiceModal } = useActions(sceneDashboardChoiceModalLogic({ scene: Scene.Person })) diff --git a/frontend/src/scenes/persons/PersonDeleteModal.tsx b/frontend/src/scenes/persons/PersonDeleteModal.tsx index 4a026ec4c5b3b..d695c33850aac 100644 --- a/frontend/src/scenes/persons/PersonDeleteModal.tsx +++ b/frontend/src/scenes/persons/PersonDeleteModal.tsx @@ -1,7 +1,9 @@ -import { useActions, useValues } from 'kea' import { LemonButton, LemonModal, Link } from '@posthog/lemon-ui' -import { PersonType } from '~/types' +import { useActions, useValues } from 'kea' import { personDeleteModalLogic } from 'scenes/persons/personDeleteModalLogic' + +import { PersonType } from '~/types' + import { asDisplay } from './person-utils' export function PersonDeleteModal(): JSX.Element | null { diff --git a/frontend/src/scenes/persons/PersonDisplay.tsx b/frontend/src/scenes/persons/PersonDisplay.tsx index 1feadfb8b3733..3d2cb1d9b758c 100644 --- a/frontend/src/scenes/persons/PersonDisplay.tsx +++ b/frontend/src/scenes/persons/PersonDisplay.tsx @@ -1,15 +1,18 @@ import './PersonDisplay.scss' -import { Link } from 'lib/lemon-ui/Link' -import { ProfilePicture, ProfilePictureProps } from 'lib/lemon-ui/ProfilePicture' + import clsx from 'clsx' +import { router } from 'kea-router' +import { Link } from 'lib/lemon-ui/Link' import { Popover } from 'lib/lemon-ui/Popover' -import { PersonPreview } from './PersonPreview' +import { ProfilePicture, ProfilePictureProps } from 'lib/lemon-ui/ProfilePicture' import { useMemo, useState } from 'react' -import { router } from 'kea-router' -import { asDisplay, asLink } from './person-utils' import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' + import { NotebookNodeType } from '~/types' +import { asDisplay, asLink } from './person-utils' +import { PersonPreview } from './PersonPreview' + type PersonPropType = | { properties?: Record; distinct_ids?: string[]; distinct_id?: never } | { properties?: Record; distinct_ids?: never; distinct_id?: string } diff --git a/frontend/src/scenes/persons/PersonFeedCanvas.tsx b/frontend/src/scenes/persons/PersonFeedCanvas.tsx index fd3ebd2448934..958da87425fb9 100644 --- a/frontend/src/scenes/persons/PersonFeedCanvas.tsx +++ b/frontend/src/scenes/persons/PersonFeedCanvas.tsx @@ -1,10 +1,10 @@ import { useValues } from 'kea' - -import { PersonType } from '~/types' -import { Notebook } from 'scenes/notebooks/Notebook/Notebook' import { uuid } from 'lib/utils' +import { Notebook } from 'scenes/notebooks/Notebook/Notebook' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { PersonType } from '~/types' + type PersonFeedCanvasProps = { person: PersonType } diff --git a/frontend/src/scenes/persons/PersonPreview.tsx b/frontend/src/scenes/persons/PersonPreview.tsx index b303e6a05ba92..2c874726304c7 100644 --- a/frontend/src/scenes/persons/PersonPreview.tsx +++ b/frontend/src/scenes/persons/PersonPreview.tsx @@ -1,14 +1,16 @@ +import { LemonButton, Link } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { personLogic } from './personLogic' -import { Spinner } from 'lib/lemon-ui/Spinner' +import { PropertiesTable } from 'lib/components/PropertiesTable' +import { IconOpenInNew } from 'lib/lemon-ui/icons' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { LemonButton, Link } from '@posthog/lemon-ui' +import { Spinner } from 'lib/lemon-ui/Spinner' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { urls } from 'scenes/urls' -import { PropertiesTable } from 'lib/components/PropertiesTable' + import { NotebookNodeType, PropertyDefinitionType } from '~/types' -import { IconOpenInNew } from 'lib/lemon-ui/icons' + import { asDisplay } from './person-utils' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' +import { personLogic } from './personLogic' export type PersonPreviewProps = { distinctId: string | undefined diff --git a/frontend/src/scenes/persons/PersonScene.tsx b/frontend/src/scenes/persons/PersonScene.tsx index 6459e73dab82e..b570b1d9d7bac 100644 --- a/frontend/src/scenes/persons/PersonScene.tsx +++ b/frontend/src/scenes/persons/PersonScene.tsx @@ -1,41 +1,44 @@ -import { Dropdown, Menu } from 'antd' +import './PersonScene.scss' + // eslint-disable-next-line no-restricted-imports import { DownOutlined } from '@ant-design/icons' +import { LemonButton, LemonDivider, LemonSelect, LemonTag, Link } from '@posthog/lemon-ui' +import { Dropdown, Menu } from 'antd' import { useActions, useValues } from 'kea' -import { personsLogic } from './personsLogic' -import { PersonDisplay } from './PersonDisplay' -import './PersonScene.scss' +import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { MergeSplitPerson } from './MergeSplitPerson' -import { PersonCohorts } from './PersonCohorts' +import { NotFound } from 'lib/components/NotFound' +import { PageHeader } from 'lib/components/PageHeader' import { PropertiesTable } from 'lib/components/PropertiesTable' import { TZLabel } from 'lib/components/TZLabel' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { NotebookNodeType, PersonsTabType, PersonType, PropertyDefinitionType } from '~/types' -import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' -import { urls } from 'scenes/urls' -import { RelatedGroups } from 'scenes/groups/RelatedGroups' import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' -import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { LemonButton, LemonDivider, LemonSelect, LemonTag, Link } from '@posthog/lemon-ui' -import { teamLogic } from 'scenes/teamLogic' +import { IconInfo } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' -import { NotFound } from 'lib/components/NotFound' -import { RelatedFeatureFlags } from './RelatedFeatureFlags' -import { Query } from '~/queries/Query/Query' -import { NodeKind } from '~/queries/schema' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { RelatedGroups } from 'scenes/groups/RelatedGroups' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' +import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal' import { personDeleteModalLogic } from 'scenes/persons/personDeleteModalLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { SessionRecordingsPlaylist } from 'scenes/session-recordings/playlist/SessionRecordingsPlaylist' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + import { defaultDataTableColumns } from '~/queries/nodes/DataTable/utils' -import { IconInfo } from 'lib/lemon-ui/icons' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Query } from '~/queries/Query/Query' +import { NodeKind } from '~/queries/schema' +import { NotebookNodeType, PersonsTabType, PersonType, PropertyDefinitionType } from '~/types' + +import { MergeSplitPerson } from './MergeSplitPerson' +import { PersonCohorts } from './PersonCohorts' import { PersonDashboard } from './PersonDashboard' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' -import { SessionRecordingsPlaylist } from 'scenes/session-recordings/playlist/SessionRecordingsPlaylist' +import { PersonDisplay } from './PersonDisplay' import PersonFeedCanvas from './PersonFeedCanvas' +import { personsLogic } from './personsLogic' +import { RelatedFeatureFlags } from './RelatedFeatureFlags' export const scene: SceneExport = { component: PersonScene, diff --git a/frontend/src/scenes/persons/PersonsSearch.tsx b/frontend/src/scenes/persons/PersonsSearch.tsx index 8b4c5166cad21..a222715637bae 100644 --- a/frontend/src/scenes/persons/PersonsSearch.tsx +++ b/frontend/src/scenes/persons/PersonsSearch.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState } from 'react' -import { useValues, useActions } from 'kea' -import { personsLogic } from './personsLogic' +import { LemonInput } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { IconInfo } from 'lib/lemon-ui/icons' import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { LemonInput } from '@posthog/lemon-ui' +import { useEffect, useState } from 'react' import { useDebouncedCallback } from 'use-debounce' +import { personsLogic } from './personsLogic' + export const PersonsSearch = (): JSX.Element => { const { loadPersons, setListFilters } = useActions(personsLogic) const { listFilters } = useValues(personsLogic) diff --git a/frontend/src/scenes/persons/PersonsTable.tsx b/frontend/src/scenes/persons/PersonsTable.tsx index c3d8a2f456fe6..3131fb28b6c87 100644 --- a/frontend/src/scenes/persons/PersonsTable.tsx +++ b/frontend/src/scenes/persons/PersonsTable.tsx @@ -1,16 +1,18 @@ -import { TZLabel } from 'lib/components/TZLabel' -import { PropertiesTable } from 'lib/components/PropertiesTable' -import { PersonType, PropertyDefinitionType } from '~/types' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { PersonDisplay } from './PersonDisplay' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { LemonButton } from '@posthog/lemon-ui' -import { IconDelete } from 'lib/lemon-ui/icons' import { useActions } from 'kea' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { PropertiesTable } from 'lib/components/PropertiesTable' +import { TZLabel } from 'lib/components/TZLabel' +import { IconDelete } from 'lib/lemon-ui/icons' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { PersonDeleteModal } from 'scenes/persons/PersonDeleteModal' import { personDeleteModalLogic } from 'scenes/persons/personDeleteModalLogic' import { personsLogic } from 'scenes/persons/personsLogic' +import { PersonType, PropertyDefinitionType } from '~/types' + +import { PersonDisplay } from './PersonDisplay' + interface PersonsTableType { people: PersonType[] loading?: boolean diff --git a/frontend/src/scenes/persons/RelatedFeatureFlags.tsx b/frontend/src/scenes/persons/RelatedFeatureFlags.tsx index 3e1505f4b1aeb..c641a38c478a2 100644 --- a/frontend/src/scenes/persons/RelatedFeatureFlags.tsx +++ b/frontend/src/scenes/persons/RelatedFeatureFlags.tsx @@ -1,13 +1,15 @@ import { LemonInput, LemonSelect, LemonTable, LemonTag, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { normalizeColumnTitle } from 'lib/components/Table/utils' +import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { capitalizeFirstLetter } from 'lib/utils' import stringWithWBR from 'lib/utils/stringWithWBR' import { urls } from 'scenes/urls' + import { FeatureFlagReleaseType } from '~/types' -import { relatedFeatureFlagsLogic, RelatedFeatureFlag } from './relatedFeatureFlagsLogic' -import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' + +import { RelatedFeatureFlag, relatedFeatureFlagsLogic } from './relatedFeatureFlagsLogic' interface Props { distinctId: string diff --git a/frontend/src/scenes/persons/activityDescriptions.tsx b/frontend/src/scenes/persons/activityDescriptions.tsx index f11568827c9dd..ffca0f970d702 100644 --- a/frontend/src/scenes/persons/activityDescriptions.tsx +++ b/frontend/src/scenes/persons/activityDescriptions.tsx @@ -1,7 +1,7 @@ import { ActivityLogItem, HumanizedChange } from 'lib/components/ActivityLog/humanizeActivity' -import { PersonDisplay } from 'scenes/persons/PersonDisplay' import { SentenceList } from 'lib/components/ActivityLog/SentenceList' import { Link } from 'lib/lemon-ui/Link' +import { PersonDisplay } from 'scenes/persons/PersonDisplay' import { urls } from 'scenes/urls' export function personActivityDescriber(logItem: ActivityLogItem): HumanizedChange { diff --git a/frontend/src/scenes/persons/mergeSplitPersonLogic.ts b/frontend/src/scenes/persons/mergeSplitPersonLogic.ts index 3e5c4dae2417f..524d7de4de293 100644 --- a/frontend/src/scenes/persons/mergeSplitPersonLogic.ts +++ b/frontend/src/scenes/persons/mergeSplitPersonLogic.ts @@ -1,10 +1,12 @@ +import { actions, connect, events, kea, key, listeners, path, props, reducers } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, listeners, events } from 'kea' import { router } from 'kea-router' import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + import { PersonType } from '~/types' + import type { mergeSplitPersonLogicType } from './mergeSplitPersonLogicType' import { personsLogic } from './personsLogic' diff --git a/frontend/src/scenes/persons/person-utils.test.ts b/frontend/src/scenes/persons/person-utils.test.ts index 2baca39945f4b..6fe01850ac000 100644 --- a/frontend/src/scenes/persons/person-utils.test.ts +++ b/frontend/src/scenes/persons/person-utils.test.ts @@ -1,7 +1,9 @@ -import { PersonType } from '~/types' import { uuid } from 'lib/utils' import { urls } from 'scenes/urls' -import { asLink, asDisplay } from './person-utils' + +import { PersonType } from '~/types' + +import { asDisplay, asLink } from './person-utils' describe('the person header', () => { describe('linking to a person', () => { diff --git a/frontend/src/scenes/persons/person-utils.ts b/frontend/src/scenes/persons/person-utils.ts index 35928473b08be..a91a1a08e029b 100644 --- a/frontend/src/scenes/persons/person-utils.ts +++ b/frontend/src/scenes/persons/person-utils.ts @@ -1,9 +1,10 @@ import './PersonDisplay.scss' -import { urls } from 'scenes/urls' -import { ProfilePictureProps } from 'lib/lemon-ui/ProfilePicture' -import { teamLogic } from 'scenes/teamLogic' + import { PERSON_DEFAULT_DISPLAY_NAME_PROPERTIES } from 'lib/constants' +import { ProfilePictureProps } from 'lib/lemon-ui/ProfilePicture' import { midEllipsis } from 'lib/utils' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' type PersonPropType = | { properties?: Record; distinct_ids?: string[]; distinct_id?: never } diff --git a/frontend/src/scenes/persons/personDashboardLogic.ts b/frontend/src/scenes/persons/personDashboardLogic.ts index 2d9b3fd123218..5c97b78ede12e 100644 --- a/frontend/src/scenes/persons/personDashboardLogic.ts +++ b/frontend/src/scenes/persons/personDashboardLogic.ts @@ -1,11 +1,12 @@ -import { connect, kea, selectors, path } from 'kea' - -import type { personDashboardLogicType } from './personDashboardLogicType' -import { DashboardPlacement, PersonType } from '~/types' +import { connect, kea, path, selectors } from 'kea' import { DashboardLogicProps } from 'scenes/dashboard/dashboardLogic' import { Scene } from 'scenes/sceneTypes' import { userLogic } from 'scenes/userLogic' +import { DashboardPlacement, PersonType } from '~/types' + +import type { personDashboardLogicType } from './personDashboardLogicType' + export interface PersonDashboardLogicProps { person: PersonType } diff --git a/frontend/src/scenes/persons/personDeleteModalLogic.tsx b/frontend/src/scenes/persons/personDeleteModalLogic.tsx index 7c9dd0343392c..07da7f430e345 100644 --- a/frontend/src/scenes/persons/personDeleteModalLogic.tsx +++ b/frontend/src/scenes/persons/personDeleteModalLogic.tsx @@ -1,11 +1,13 @@ -import { actions, kea, props, reducers, path } from 'kea' +import { actions, kea, path, props, reducers } from 'kea' +import { loaders } from 'kea-loaders' import api from 'lib/api' -import { PersonType } from '~/types' -import { toParams } from 'lib/utils' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import type { personDeleteModalLogicType } from './personDeleteModalLogicType' -import { loaders } from 'kea-loaders' +import { toParams } from 'lib/utils' + +import { PersonType } from '~/types' + import { asDisplay } from './person-utils' +import type { personDeleteModalLogicType } from './personDeleteModalLogicType' export interface PersonDeleteModalLogicProps { person: PersonType diff --git a/frontend/src/scenes/persons/personLogic.tsx b/frontend/src/scenes/persons/personLogic.tsx index 9718cdd720de6..93e655f8757f9 100644 --- a/frontend/src/scenes/persons/personLogic.tsx +++ b/frontend/src/scenes/persons/personLogic.tsx @@ -1,7 +1,8 @@ import { actions, afterMount, kea, key, path, props } from 'kea' +import { loaders } from 'kea-loaders' import api from 'lib/api' + import { PersonType } from '~/types' -import { loaders } from 'kea-loaders' import type { personLogicType } from './personLogicType' diff --git a/frontend/src/scenes/persons/personsLogic.test.ts b/frontend/src/scenes/persons/personsLogic.test.ts index f8bc7fbc3a53f..d34a46fe6c748 100644 --- a/frontend/src/scenes/persons/personsLogic.test.ts +++ b/frontend/src/scenes/persons/personsLogic.test.ts @@ -1,12 +1,13 @@ +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' +import api from 'lib/api' +import { MOCK_TEAM_ID } from 'lib/api.mock' + +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' -import { personsLogic } from './personsLogic' -import { router } from 'kea-router' import { PropertyFilterType, PropertyOperator } from '~/types' -import { useMocks } from '~/mocks/jest' -import api from 'lib/api' -import { MOCK_TEAM_ID } from 'lib/api.mock' +import { personsLogic } from './personsLogic' describe('personsLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/persons/personsLogic.tsx b/frontend/src/scenes/persons/personsLogic.tsx index dc3dbfc01ea4b..e87d7f3d749f3 100644 --- a/frontend/src/scenes/persons/personsLogic.tsx +++ b/frontend/src/scenes/persons/personsLogic.tsx @@ -1,30 +1,32 @@ +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import { decodeParams, router, actionToUrl, urlToAction } from 'kea-router' +import { actionToUrl, decodeParams, router, urlToAction } from 'kea-router' import api, { CountedPaginatedResponse } from 'lib/api' -import type { personsLogicType } from './personsLogicType' +import { TriggerExportProps } from 'lib/components/ExportButton/exporter' +import { convertPropertyGroupToProperties, isValidPropertyFilter } from 'lib/components/PropertyFilters/utils' +import { FEATURE_FLAGS } from 'lib/constants' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { Scene } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { hogqlQuery } from '~/queries/query' import { - PersonPropertyFilter, + AnyPropertyFilter, Breadcrumb, CohortType, ExporterFormat, PersonListParams, + PersonPropertyFilter, PersonsTabType, PersonType, - AnyPropertyFilter, } from '~/types' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { urls } from 'scenes/urls' -import { teamLogic } from 'scenes/teamLogic' -import { toParams } from 'lib/utils' -import { convertPropertyGroupToProperties, isValidPropertyFilter } from 'lib/components/PropertyFilters/utils' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { TriggerExportProps } from 'lib/components/ExportButton/exporter' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' + import { asDisplay } from './person-utils' -import { hogqlQuery } from '~/queries/query' -import { Scene } from 'scenes/sceneTypes' +import type { personsLogicType } from './personsLogicType' export interface PersonsLogicProps { cohort?: number | 'new' diff --git a/frontend/src/scenes/persons/relatedFeatureFlagsLogic.ts b/frontend/src/scenes/persons/relatedFeatureFlagsLogic.ts index 4f8fc9817ea4a..7f60dfda38c67 100644 --- a/frontend/src/scenes/persons/relatedFeatureFlagsLogic.ts +++ b/frontend/src/scenes/persons/relatedFeatureFlagsLogic.ts @@ -1,13 +1,14 @@ import Fuse from 'fuse.js' -import { actions, connect, events, kea, key, path, props, selectors, reducers } from 'kea' +import { actions, connect, events, kea, key, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' import { toParams } from 'lib/utils' import { featureFlagsLogic } from 'scenes/feature-flags/featureFlagsLogic' import { teamLogic } from 'scenes/teamLogic' + import { FeatureFlagReleaseType, FeatureFlagType } from '~/types' -import { FeatureFlagMatchReason } from './RelatedFeatureFlags' +import { FeatureFlagMatchReason } from './RelatedFeatureFlags' import type { relatedFeatureFlagsLogicType } from './relatedFeatureFlagsLogicType' export interface RelatedFeatureFlag extends FeatureFlagType { value: boolean | string diff --git a/frontend/src/scenes/pipeline/NewButton.tsx b/frontend/src/scenes/pipeline/NewButton.tsx index 1b453ae8beee0..96ea31b811189 100644 --- a/frontend/src/scenes/pipeline/NewButton.tsx +++ b/frontend/src/scenes/pipeline/NewButton.tsx @@ -1,8 +1,10 @@ import { LemonButton } from 'lib/lemon-ui/LemonButton/LemonButton' import { urls } from 'scenes/urls' -import { singularName } from './pipelineLogic' + import { PipelineTabs } from '~/types' +import { singularName } from './pipelineLogic' + type NewButtonProps = { tab: PipelineTabs } diff --git a/frontend/src/scenes/pipeline/Pipeline.stories.tsx b/frontend/src/scenes/pipeline/Pipeline.stories.tsx index 97b8f668fa4f7..c59cacefe547e 100644 --- a/frontend/src/scenes/pipeline/Pipeline.stories.tsx +++ b/frontend/src/scenes/pipeline/Pipeline.stories.tsx @@ -1,11 +1,13 @@ -import { useEffect } from 'react' import { Meta } from '@storybook/react' -import { App } from 'scenes/App' import { router } from 'kea-router' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' + +import { mswDecorator, useStorybookMocks } from '~/mocks/browser' import { PipelineAppTabs, PipelineTabs } from '~/types' + import { pipelineLogic } from './pipelineLogic' -import { mswDecorator, useStorybookMocks } from '~/mocks/browser' export default { title: 'Scenes-App/Pipeline', diff --git a/frontend/src/scenes/pipeline/Pipeline.tsx b/frontend/src/scenes/pipeline/Pipeline.tsx index a688d84b81cd6..1e7a73d7d357e 100644 --- a/frontend/src/scenes/pipeline/Pipeline.tsx +++ b/frontend/src/scenes/pipeline/Pipeline.tsx @@ -1,14 +1,16 @@ -import { SceneExport } from 'scenes/sceneTypes' +import { useValues } from 'kea' +import { router } from 'kea-router' import { PageHeader } from 'lib/components/PageHeader' -import { humanFriendlyTabName, pipelineLogic } from './pipelineLogic' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { useValues } from 'kea' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' -import { router } from 'kea-router' + import { PipelineTabs } from '~/types' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { Transformations } from './Transformations' + import { NewButton } from './NewButton' +import { humanFriendlyTabName, pipelineLogic } from './pipelineLogic' +import { Transformations } from './Transformations' export function Pipeline(): JSX.Element { const { currentTab } = useValues(pipelineLogic) diff --git a/frontend/src/scenes/pipeline/PipelineApp.tsx b/frontend/src/scenes/pipeline/PipelineApp.tsx index e60a2277377e3..3ca9430e7d8a2 100644 --- a/frontend/src/scenes/pipeline/PipelineApp.tsx +++ b/frontend/src/scenes/pipeline/PipelineApp.tsx @@ -1,14 +1,16 @@ -import { SceneExport } from 'scenes/sceneTypes' +import { Spinner } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { pipelineAppLogic } from './pipelineAppLogic' +import { router } from 'kea-router' import { PageHeader } from 'lib/components/PageHeader' import { LemonTabs } from 'lib/lemon-ui/LemonTabs/LemonTabs' -import { router } from 'kea-router' -import { PipelineAppTabs } from '~/types' -import { urls } from 'scenes/urls' -import { PluginLogs } from 'scenes/plugins/plugin/PluginLogs' -import { Spinner } from '@posthog/lemon-ui' import { capitalizeFirstLetter } from 'lib/utils' +import { PluginLogs } from 'scenes/plugins/plugin/PluginLogs' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { PipelineAppTabs } from '~/types' + +import { pipelineAppLogic } from './pipelineAppLogic' export const scene: SceneExport = { component: PipelineApp, diff --git a/frontend/src/scenes/pipeline/Transformations.tsx b/frontend/src/scenes/pipeline/Transformations.tsx index c16333ff78a00..93a47862e0fde 100644 --- a/frontend/src/scenes/pipeline/Transformations.tsx +++ b/frontend/src/scenes/pipeline/Transformations.tsx @@ -1,3 +1,7 @@ +import { DndContext, DragEndEvent } from '@dnd-kit/core' +import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' +import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' +import { CSS } from '@dnd-kit/utilities' import { LemonBadge, LemonButton, @@ -10,22 +14,20 @@ import { Tooltip, } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { pipelineTransformationsLogic } from './transformationsLogic' -import { PluginImage } from 'scenes/plugins/plugin/PluginImage' -import { PipelineAppTabs, PipelineTabs, PluginConfigTypeNew, PluginType, ProductKey } from '~/types' -import { urls } from 'scenes/urls' -import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' -import { DndContext, DragEndEvent } from '@dnd-kit/core' -import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' -import { CSS } from '@dnd-kit/utilities' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { dayjs } from 'lib/dayjs' import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown/LemonMarkdown' import { updatedAtColumn } from 'lib/lemon-ui/LemonTable/columnUtils' import { humanFriendlyDetailedTime } from 'lib/utils' -import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown/LemonMarkdown' -import { dayjs } from 'lib/dayjs' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' -import { NewButton } from './NewButton' import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { PluginImage } from 'scenes/plugins/plugin/PluginImage' +import { urls } from 'scenes/urls' + +import { PipelineAppTabs, PipelineTabs, PluginConfigTypeNew, PluginType, ProductKey } from '~/types' + +import { NewButton } from './NewButton' +import { pipelineTransformationsLogic } from './transformationsLogic' export function Transformations(): JSX.Element { const { diff --git a/frontend/src/scenes/pipeline/pipelineAppLogic.tsx b/frontend/src/scenes/pipeline/pipelineAppLogic.tsx index 260d214df3785..b7f3b0110ae58 100644 --- a/frontend/src/scenes/pipeline/pipelineAppLogic.tsx +++ b/frontend/src/scenes/pipeline/pipelineAppLogic.tsx @@ -1,10 +1,11 @@ -import { kea, reducers, path, props, key, actions, selectors } from 'kea' - -import type { pipelineAppLogicType } from './pipelineAppLogicType' -import { Breadcrumb, PipelineAppTabs } from '~/types' -import { urls } from 'scenes/urls' +import { actions, kea, key, path, props, reducers, selectors } from 'kea' import { actionToUrl, urlToAction } from 'kea-router' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb, PipelineAppTabs } from '~/types' + +import type { pipelineAppLogicType } from './pipelineAppLogicType' export interface PipelineAppLogicProps { id: number diff --git a/frontend/src/scenes/pipeline/pipelineLogic.tsx b/frontend/src/scenes/pipeline/pipelineLogic.tsx index cf99abf943bfc..ff96bd6077d86 100644 --- a/frontend/src/scenes/pipeline/pipelineLogic.tsx +++ b/frontend/src/scenes/pipeline/pipelineLogic.tsx @@ -1,9 +1,11 @@ import { actions, kea, path, reducers, selectors } from 'kea' -import type { pipelineLogicType } from './pipelineLogicType' import { actionToUrl, urlToAction } from 'kea-router' +import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + import { Breadcrumb, PipelineTabs } from '~/types' -import { Scene } from 'scenes/sceneTypes' + +import type { pipelineLogicType } from './pipelineLogicType' export const singularName = (tab: PipelineTabs): string => { switch (tab) { diff --git a/frontend/src/scenes/pipeline/transformationsLogic.tsx b/frontend/src/scenes/pipeline/transformationsLogic.tsx index eadb6799ed898..5bb16e830253b 100644 --- a/frontend/src/scenes/pipeline/transformationsLogic.tsx +++ b/frontend/src/scenes/pipeline/transformationsLogic.tsx @@ -1,14 +1,15 @@ import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' -import { PluginConfigTypeNew, PluginType, ProductKey } from '~/types' import posthog from 'posthog-js' - -import type { pipelineTransformationsLogicType } from './transformationsLogicType' -import { teamLogic } from 'scenes/teamLogic' import { canConfigurePlugins } from 'scenes/plugins/access' +import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' +import { PluginConfigTypeNew, PluginType, ProductKey } from '~/types' + +import type { pipelineTransformationsLogicType } from './transformationsLogicType' + function capturePluginEvent(event: string, plugin: PluginType, pluginConfig: PluginConfigTypeNew): void { posthog.capture(event, { plugin_id: plugin.id, diff --git a/frontend/src/scenes/plugins/AppsScene.tsx b/frontend/src/scenes/plugins/AppsScene.tsx index 374681460c088..54d476409bb0d 100644 --- a/frontend/src/scenes/plugins/AppsScene.tsx +++ b/frontend/src/scenes/plugins/AppsScene.tsx @@ -1,21 +1,22 @@ -import { useEffect } from 'react' +import './Plugins.scss' + +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { pluginsLogic } from './pluginsLogic' -import { PageHeader } from 'lib/components/PageHeader' -import { canGloballyManagePlugins, canViewPlugins } from './access' -import { userLogic } from 'scenes/userLogic' -import { SceneExport } from 'scenes/sceneTypes' import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { PageHeader } from 'lib/components/PageHeader' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { BatchExportsTab } from './tabs/batch-exports/BatchExportsTab' -import { AppsTab } from './tabs/apps/AppsTab' -import { PluginTab } from './types' -import { LemonButton } from '@posthog/lemon-ui' +import { useEffect } from 'react' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' -import './Plugins.scss' +import { canGloballyManagePlugins, canViewPlugins } from './access' +import { pluginsLogic } from './pluginsLogic' import { AppsManagementTab } from './tabs/apps/AppsManagementTab' +import { AppsTab } from './tabs/apps/AppsTab' +import { BatchExportsTab } from './tabs/batch-exports/BatchExportsTab' +import { PluginTab } from './types' export const scene: SceneExport = { component: AppsScene, diff --git a/frontend/src/scenes/plugins/Plugins.stories.tsx b/frontend/src/scenes/plugins/Plugins.stories.tsx index 2344623bb0fcc..714eff490674e 100644 --- a/frontend/src/scenes/plugins/Plugins.stories.tsx +++ b/frontend/src/scenes/plugins/Plugins.stories.tsx @@ -1,8 +1,9 @@ import { Meta, Story } from '@storybook/react' -import { App } from 'scenes/App' -import { useEffect } from 'react' import { router } from 'kea-router' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' + import { useAvailableFeatures } from '~/mocks/features' import { AvailableFeature } from '~/types' diff --git a/frontend/src/scenes/plugins/PluginsSearch.tsx b/frontend/src/scenes/plugins/PluginsSearch.tsx index 0cb80c2bf9679..66559480381d5 100644 --- a/frontend/src/scenes/plugins/PluginsSearch.tsx +++ b/frontend/src/scenes/plugins/PluginsSearch.tsx @@ -1,6 +1,6 @@ +import { LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { LemonInput } from '@posthog/lemon-ui' export function PluginsSearch(): JSX.Element { const { searchTerm } = useValues(pluginsLogic) diff --git a/frontend/src/scenes/plugins/access.ts b/frontend/src/scenes/plugins/access.ts index 3d0f80fdb9c5d..a66dae1d5a17f 100644 --- a/frontend/src/scenes/plugins/access.ts +++ b/frontend/src/scenes/plugins/access.ts @@ -1,4 +1,5 @@ import { PluginsAccessLevel } from 'lib/constants' + import { OrganizationType } from '../../types' export function canGloballyManagePlugins(organization: OrganizationType | null | undefined): boolean { diff --git a/frontend/src/scenes/plugins/edit/PluginDrawer.tsx b/frontend/src/scenes/plugins/edit/PluginDrawer.tsx index b5a5b7f31f5a0..0fbf516201734 100644 --- a/frontend/src/scenes/plugins/edit/PluginDrawer.tsx +++ b/frontend/src/scenes/plugins/edit/PluginDrawer.tsx @@ -1,25 +1,26 @@ -import React, { useEffect, useState } from 'react' +import { IconCode } from '@posthog/icons' +import { LemonButton, LemonSwitch, LemonTag, Link } from '@posthog/lemon-ui' +import { PluginConfigChoice, PluginConfigSchema } from '@posthog/plugin-scaffold' +import { Form } from 'antd' import { useActions, useValues } from 'kea' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { Form, Switch } from 'antd' -import { userLogic } from 'scenes/userLogic' -import { PluginImage } from 'scenes/plugins/plugin/PluginImage' import { Drawer } from 'lib/components/Drawer' -import { defaultConfigForPlugin, doFieldRequirementsMatch, getConfigSchemaArray } from 'scenes/plugins/utils' -import { PluginSource } from '../source/PluginSource' -import { PluginConfigChoice, PluginConfigSchema } from '@posthog/plugin-scaffold' -import { PluginField } from 'scenes/plugins/edit/PluginField' +import { MOCK_NODE_PROCESS } from 'lib/constants' +import { IconLock } from 'lib/lemon-ui/icons' +import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { endWithPunctation } from 'lib/utils' +import React, { useEffect, useState } from 'react' +import { PluginField } from 'scenes/plugins/edit/PluginField' +import { PluginImage } from 'scenes/plugins/plugin/PluginImage' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { defaultConfigForPlugin, doFieldRequirementsMatch, getConfigSchemaArray } from 'scenes/plugins/utils' +import { userLogic } from 'scenes/userLogic' + import { canGloballyManagePlugins } from '../access' +import { PluginSource } from '../source/PluginSource' +import { PluginTags } from '../tabs/apps/components' import { capabilitiesInfo } from './CapabilitiesInfo' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { PluginJobOptions } from './interface-jobs/PluginJobOptions' -import { MOCK_NODE_PROCESS } from 'lib/constants' -import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' -import { PluginTags } from '../tabs/apps/components' -import { IconLock } from 'lib/lemon-ui/icons' -import { LemonButton, LemonTag, Link } from '@posthog/lemon-ui' -import { IconCode } from '@posthog/icons' window.process = MOCK_NODE_PROCESS @@ -31,10 +32,10 @@ function EnabledDisabledSwitch({ onChange?: (value: boolean) => void }): JSX.Element { return ( - <> - - {value ? 'Enabled' : 'Disabled'} - +
    + + {value ? 'Enabled' : 'Disabled'} +
    ) } diff --git a/frontend/src/scenes/plugins/edit/PluginField.tsx b/frontend/src/scenes/plugins/edit/PluginField.tsx index f4ccae78890b0..48773fc765898 100644 --- a/frontend/src/scenes/plugins/edit/PluginField.tsx +++ b/frontend/src/scenes/plugins/edit/PluginField.tsx @@ -1,11 +1,11 @@ -import { UploadField } from 'scenes/plugins/edit/UploadField' -import { Button, Input, Select } from 'antd' -import { useState } from 'react' import { PluginConfigSchema } from '@posthog/plugin-scaffold/src/types' -import { SECRET_FIELD_VALUE } from 'scenes/plugins/utils' -import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' +import { Button, Input, Select } from 'antd' import { CodeEditor } from 'lib/components/CodeEditors' import { IconEdit } from 'lib/lemon-ui/icons' +import { useState } from 'react' +import { AutoSizer } from 'react-virtualized/dist/es/AutoSizer' +import { UploadField } from 'scenes/plugins/edit/UploadField' +import { SECRET_FIELD_VALUE } from 'scenes/plugins/utils' function JsonConfigField(props: { onChange: (value: any) => void diff --git a/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobConfiguration.tsx b/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobConfiguration.tsx index 2f4a3a222fc9a..cb09686bd9b19 100644 --- a/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobConfiguration.tsx +++ b/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobConfiguration.tsx @@ -1,21 +1,23 @@ -import { useMemo } from 'react' +import { IconCheck } from '@posthog/icons' +import { LemonSegmentedButton, Tooltip } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { ChildFunctionProps, Form } from 'kea-forms' +import { CodeEditor } from 'lib/components/CodeEditors' +import { DatePicker } from 'lib/components/DatePicker' +import { dayjs } from 'lib/dayjs' import { Field } from 'lib/forms/Field' -import { useValues, useActions } from 'kea' -import { userLogic } from 'scenes/userLogic' -import { JobPayloadFieldOptions } from '~/types' -import { interfaceJobsLogic, InterfaceJobsProps } from './interfaceJobsLogic' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { IconClose, IconPlayCircle, IconSettings } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonCalendarRangeInline } from 'lib/lemon-ui/LemonCalendarRange/LemonCalendarRangeInline' -import { dayjs } from 'lib/dayjs' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonModal } from 'lib/lemon-ui/LemonModal' import { formatDate, formatDateRange } from 'lib/utils' -import { DatePicker } from 'lib/components/DatePicker' -import { CodeEditor } from 'lib/components/CodeEditors' -import { IconClose, IconPlayCircle, IconSettings } from 'lib/lemon-ui/icons' -import { IconCheck } from '@posthog/icons' -import { LemonSegmentedButton, Tooltip } from '@posthog/lemon-ui' +import { useMemo } from 'react' +import { userLogic } from 'scenes/userLogic' + +import { JobPayloadFieldOptions } from '~/types' + +import { interfaceJobsLogic, InterfaceJobsProps } from './interfaceJobsLogic' // keep in sync with plugin-server's export-historical-events.ts export const HISTORICAL_EXPORT_JOB_NAME = 'Export historical events' diff --git a/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobOptions.tsx b/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobOptions.tsx index 1f13476a61e8f..85b9fce6a0b50 100644 --- a/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobOptions.tsx +++ b/frontend/src/scenes/plugins/edit/interface-jobs/PluginJobOptions.tsx @@ -1,15 +1,17 @@ import { LemonTag } from '@posthog/lemon-ui' -import { Link } from 'lib/lemon-ui/Link' import { useValues } from 'kea' +import { FEATURE_FLAGS } from 'lib/constants' +import { Link } from 'lib/lemon-ui/Link' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { useMemo } from 'react' + import { JobSpec } from '~/types' + import { HISTORICAL_EXPORT_JOB_NAME, HISTORICAL_EXPORT_JOB_NAME_V2, PluginJobConfiguration, } from './PluginJobConfiguration' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' interface PluginJobOptionsProps { pluginId: number diff --git a/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts b/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts index b4ad3e623147a..c18eb2f98e50f 100644 --- a/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts +++ b/frontend/src/scenes/plugins/edit/interface-jobs/interfaceJobsLogic.ts @@ -1,12 +1,14 @@ import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' -import { actions, kea, key, events, listeners, path, props, reducers } from 'kea' +import { actions, events, kea, key, listeners, path, props, reducers } from 'kea' import { forms } from 'kea-forms' import api from 'lib/api' -import type { interfaceJobsLogicType } from './interfaceJobsLogicType' -import { JobSpec } from '~/types' import { lemonToast } from 'lib/lemon-ui/lemonToast' import { validateJson } from 'lib/utils' +import { JobSpec } from '~/types' + +import type { interfaceJobsLogicType } from './interfaceJobsLogicType' + export interface InterfaceJobsProps { jobName: string jobSpec: JobSpec diff --git a/frontend/src/scenes/plugins/plugin/LogsDrawer.tsx b/frontend/src/scenes/plugins/plugin/LogsDrawer.tsx index 30ccebb2e0827..432294928549a 100644 --- a/frontend/src/scenes/plugins/plugin/LogsDrawer.tsx +++ b/frontend/src/scenes/plugins/plugin/LogsDrawer.tsx @@ -1,7 +1,8 @@ +import { Drawer } from 'antd' import { useActions, useValues } from 'kea' + import { pluginsLogic } from '../pluginsLogic' import { PluginLogs } from './PluginLogs' -import { Drawer } from 'antd' export function LogsDrawer(): JSX.Element { const { showingLogsPlugin, lastShownLogsPlugin } = useValues(pluginsLogic) diff --git a/frontend/src/scenes/plugins/plugin/PluginImage.tsx b/frontend/src/scenes/plugins/plugin/PluginImage.tsx index 9fec8b6275e9e..b7401e2e6561d 100644 --- a/frontend/src/scenes/plugins/plugin/PluginImage.tsx +++ b/frontend/src/scenes/plugins/plugin/PluginImage.tsx @@ -1,8 +1,9 @@ +import { IconTerminal } from 'lib/lemon-ui/icons' import { parseGithubRepoURL } from 'lib/utils' -import { useEffect, useState } from 'react' import imgPluginDefault from 'public/plugin-default.svg' +import { useEffect, useState } from 'react' + import { PluginType } from '~/types' -import { IconTerminal } from 'lib/lemon-ui/icons' export function PluginImage({ plugin, diff --git a/frontend/src/scenes/plugins/plugin/PluginLogs.tsx b/frontend/src/scenes/plugins/plugin/PluginLogs.tsx index 87c61298cd182..12a4018d81fce 100644 --- a/frontend/src/scenes/plugins/plugin/PluginLogs.tsx +++ b/frontend/src/scenes/plugins/plugin/PluginLogs.tsx @@ -1,10 +1,11 @@ +import { LemonButton, LemonCheckbox, LemonInput, LemonTable, LemonTableColumns } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { LOGS_PORTION_LIMIT } from 'lib/constants' +import { dayjs } from 'lib/dayjs' import { pluralize } from 'lib/utils' + import { PluginLogEntryType } from '../../../types' import { pluginLogsLogic, PluginLogsProps } from './pluginLogsLogic' -import { dayjs } from 'lib/dayjs' -import { LemonButton, LemonCheckbox, LemonInput, LemonTable, LemonTableColumns } from '@posthog/lemon-ui' -import { LOGS_PORTION_LIMIT } from 'lib/constants' function PluginLogEntryTypeDisplay(type: PluginLogEntryType): JSX.Element { let color: string | undefined diff --git a/frontend/src/scenes/plugins/plugin/SuccessRateBadge.tsx b/frontend/src/scenes/plugins/plugin/SuccessRateBadge.tsx index 50f0b06df583f..18a2877003358 100644 --- a/frontend/src/scenes/plugins/plugin/SuccessRateBadge.tsx +++ b/frontend/src/scenes/plugins/plugin/SuccessRateBadge.tsx @@ -1,7 +1,7 @@ +import { LemonBadge, LemonBadgeProps } from '@posthog/lemon-ui' +import { Link } from 'lib/lemon-ui/Link' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { urls } from 'scenes/urls' -import { Link } from 'lib/lemon-ui/Link' -import { LemonBadge, LemonBadgeProps } from '@posthog/lemon-ui' export function SuccessRateBadge({ deliveryRate, diff --git a/frontend/src/scenes/plugins/plugin/pluginLogsLogic.ts b/frontend/src/scenes/plugins/plugin/pluginLogsLogic.ts index f2e46d6deb683..c8db5be4b0f0a 100644 --- a/frontend/src/scenes/plugins/plugin/pluginLogsLogic.ts +++ b/frontend/src/scenes/plugins/plugin/pluginLogsLogic.ts @@ -1,11 +1,13 @@ +import { CheckboxValueType } from 'antd/lib/checkbox/Group' +import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners, events } from 'kea' +import { LOGS_PORTION_LIMIT } from 'lib/constants' + import api from '~/lib/api' import { PluginLogEntry, PluginLogEntryType } from '~/types' + import { teamLogic } from '../../teamLogic' import type { pluginLogsLogicType } from './pluginLogsLogicType' -import { CheckboxValueType } from 'antd/lib/checkbox/Group' -import { LOGS_PORTION_LIMIT } from 'lib/constants' export interface PluginLogsProps { pluginConfigId: number diff --git a/frontend/src/scenes/plugins/pluginActivityDescriptions.tsx b/frontend/src/scenes/plugins/pluginActivityDescriptions.tsx index 839f1f7861399..d80f656067f67 100644 --- a/frontend/src/scenes/plugins/pluginActivityDescriptions.tsx +++ b/frontend/src/scenes/plugins/pluginActivityDescriptions.tsx @@ -1,6 +1,7 @@ -import { dayjs } from 'lib/dayjs' import { ActivityLogItem, ActivityScope, HumanizedChange } from 'lib/components/ActivityLog/humanizeActivity' import { SentenceList } from 'lib/components/ActivityLog/SentenceList' +import { dayjs } from 'lib/dayjs' + import { SECRET_FIELD_VALUE } from './utils' export function pluginActivityDescriber(logItem: ActivityLogItem): HumanizedChange { diff --git a/frontend/src/scenes/plugins/pluginsLogic.ts b/frontend/src/scenes/plugins/pluginsLogic.ts index dcb20bcfccda5..b50d7ce36fd93 100644 --- a/frontend/src/scenes/plugins/pluginsLogic.ts +++ b/frontend/src/scenes/plugins/pluginsLogic.ts @@ -1,9 +1,21 @@ +import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import { actionToUrl, router, urlToAction } from 'kea-router' -import type { pluginsLogicType } from './pluginsLogicType' import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import posthog from 'posthog-js' +import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' +import { createDefaultPluginSource } from 'scenes/plugins/source/createDefaultPluginSource' +import { getConfigSchemaArray, getConfigSchemaObject, getPluginConfigFormData } from 'scenes/plugins/utils' +import { urls } from 'scenes/urls' +import { userLogic } from 'scenes/userLogic' + import { PersonalAPIKeyType, PluginConfigType, PluginType } from '~/types' + +import { teamLogic } from '../teamLogic' +import { canGloballyManagePlugins, canInstallPlugins } from './access' +import type { pluginsLogicType } from './pluginsLogicType' import { PluginInstallationType, PluginRepositoryEntry, @@ -11,16 +23,6 @@ import { PluginTypeWithConfig, PluginUpdateStatusType, } from './types' -import { userLogic } from 'scenes/userLogic' -import { getConfigSchemaArray, getConfigSchemaObject, getPluginConfigFormData } from 'scenes/plugins/utils' -import posthog from 'posthog-js' -import type { FormInstance } from 'antd/lib/form/hooks/useForm.d' -import { canGloballyManagePlugins, canInstallPlugins } from './access' -import { teamLogic } from '../teamLogic' -import { createDefaultPluginSource } from 'scenes/plugins/source/createDefaultPluginSource' -import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' -import { urls } from 'scenes/urls' -import { lemonToast } from 'lib/lemon-ui/lemonToast' export type PluginForm = FormInstance @@ -40,7 +42,7 @@ function capturePluginEvent(event: string, plugin: PluginType, type?: PluginInst export const pluginsLogic = kea([ path(['scenes', 'plugins', 'pluginsLogic']), - connect(frontendAppsLogic), + connect(() => frontendAppsLogic), actions({ editPlugin: (id: number | null, pluginConfigChanges: Record = {}) => ({ id, pluginConfigChanges }), savePluginConfig: (pluginConfigChanges: Record) => ({ pluginConfigChanges }), diff --git a/frontend/src/scenes/plugins/source/PluginSource.tsx b/frontend/src/scenes/plugins/source/PluginSource.tsx index ca59aea7988aa..ad47b039462bf 100644 --- a/frontend/src/scenes/plugins/source/PluginSource.tsx +++ b/frontend/src/scenes/plugins/source/PluginSource.tsx @@ -1,20 +1,21 @@ import './PluginSource.scss' -import { useEffect } from 'react' -import { useActions, useValues } from 'kea' -import { Button, Skeleton } from 'antd' + import { useMonaco } from '@monaco-editor/react' +import { Link } from '@posthog/lemon-ui' +import { Button, Skeleton } from 'antd' +import { useActions, useValues } from 'kea' +import { Form } from 'kea-forms' +import { CodeEditor } from 'lib/components/CodeEditors' import { Drawer } from 'lib/components/Drawer' - -import { userLogic } from 'scenes/userLogic' -import { canGloballyManagePlugins } from '../access' -import { pluginSourceLogic } from 'scenes/plugins/source/pluginSourceLogic' import { Field } from 'lib/forms/Field' -import { PluginSourceTabs } from 'scenes/plugins/source/PluginSourceTabs' import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { useEffect } from 'react' import { createDefaultPluginSource } from 'scenes/plugins/source/createDefaultPluginSource' -import { Form } from 'kea-forms' -import { CodeEditor } from 'lib/components/CodeEditors' -import { Link } from '@posthog/lemon-ui' +import { pluginSourceLogic } from 'scenes/plugins/source/pluginSourceLogic' +import { PluginSourceTabs } from 'scenes/plugins/source/PluginSourceTabs' +import { userLogic } from 'scenes/userLogic' + +import { canGloballyManagePlugins } from '../access' interface PluginSourceProps { pluginId: number diff --git a/frontend/src/scenes/plugins/source/PluginSourceTabs.tsx b/frontend/src/scenes/plugins/source/PluginSourceTabs.tsx index a548f242e1a74..e254b27958d7a 100644 --- a/frontend/src/scenes/plugins/source/PluginSourceTabs.tsx +++ b/frontend/src/scenes/plugins/source/PluginSourceTabs.tsx @@ -1,6 +1,7 @@ import { BuiltLogic, useActions, useValues } from 'kea' import { LemonButton } from 'lib/lemon-ui/LemonButton' import React from 'react' + import type { pluginSourceLogicType } from './pluginSourceLogicType' export function PluginSourceTabs({ logic }: { logic: BuiltLogic }): JSX.Element { diff --git a/frontend/src/scenes/plugins/source/pluginSourceLogic.tsx b/frontend/src/scenes/plugins/source/pluginSourceLogic.tsx index 27cf774627b4f..38f36fc30b169 100644 --- a/frontend/src/scenes/plugins/source/pluginSourceLogic.tsx +++ b/frontend/src/scenes/plugins/source/pluginSourceLogic.tsx @@ -1,16 +1,16 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' - -import type { pluginSourceLogicType } from './pluginSourceLogicType' import { forms } from 'kea-forms' -import api from 'lib/api' import { loaders } from 'kea-loaders' +import { beforeUnload } from 'kea-router' +import api from 'lib/api' +import { FormErrors } from 'lib/forms/Errors' import { lemonToast } from 'lib/lemon-ui/lemonToast' import { validateJson } from 'lib/utils' -import { FormErrors } from 'lib/forms/Errors' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { frontendAppsLogic } from 'scenes/apps/frontendAppsLogic' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { formatSource } from 'scenes/plugins/source/formatSource' -import { beforeUnload } from 'kea-router' + +import type { pluginSourceLogicType } from './pluginSourceLogicType' export interface PluginSourceProps { pluginId: number diff --git a/frontend/src/scenes/plugins/tabs/apps/AdvancedInstallModal.tsx b/frontend/src/scenes/plugins/tabs/apps/AdvancedInstallModal.tsx index 59f18196126e6..9649f799d7a74 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AdvancedInstallModal.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AdvancedInstallModal.tsx @@ -1,10 +1,10 @@ +import { LemonButton, LemonInput, LemonLabel, Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { useValues, useActions } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonButton, LemonInput, LemonLabel, Link } from '@posthog/lemon-ui' import { PluginInstallationType } from 'scenes/plugins/types' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' export function AdvancedInstallModal(): JSX.Element { const { preflight } = useValues(preflightLogic) diff --git a/frontend/src/scenes/plugins/tabs/apps/AppManagementView.tsx b/frontend/src/scenes/plugins/tabs/apps/AppManagementView.tsx index 4d0bc00644da6..f552339777f7c 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AppManagementView.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AppManagementView.tsx @@ -1,15 +1,17 @@ import { LemonButton, Link } from '@posthog/lemon-ui' +import { Popconfirm } from 'antd' import { useActions, useValues } from 'kea' import { IconCheckmark, IconCloudDownload, IconDelete, IconReplay, IconWeb } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { canGloballyManagePlugins } from 'scenes/plugins/access' import { PluginImage } from 'scenes/plugins/plugin/PluginImage' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { PluginTypeWithConfig, PluginRepositoryEntry, PluginInstallationType } from 'scenes/plugins/types' +import { PluginInstallationType, PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' +import { userLogic } from 'scenes/userLogic' + import { PluginType } from '~/types' + import { PluginTags } from './components' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Popconfirm } from 'antd' -import { canGloballyManagePlugins } from 'scenes/plugins/access' -import { userLogic } from 'scenes/userLogic' export function AppManagementView({ plugin, diff --git a/frontend/src/scenes/plugins/tabs/apps/AppView.tsx b/frontend/src/scenes/plugins/tabs/apps/AppView.tsx index f4de0dfacaa8c..8ff62f6727844 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AppView.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AppView.tsx @@ -1,15 +1,17 @@ -import { Link, LemonButton, LemonBadge } from '@posthog/lemon-ui' +import { LemonBadge, LemonButton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonMenuItem, LemonMenu } from 'lib/lemon-ui/LemonMenu' -import { IconLink, IconSettings, IconEllipsis, IconLegend, IconErrorOutline } from 'lib/lemon-ui/icons' +import { IconEllipsis, IconErrorOutline, IconLegend, IconLink, IconSettings } from 'lib/lemon-ui/icons' +import { LemonMenu, LemonMenuItem } from 'lib/lemon-ui/LemonMenu' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { PluginImage } from 'scenes/plugins/plugin/PluginImage' import { SuccessRateBadge } from 'scenes/plugins/plugin/SuccessRateBadge' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { PluginTypeWithConfig, PluginRepositoryEntry } from 'scenes/plugins/types' +import { PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' import { urls } from 'scenes/urls' + import { PluginType } from '~/types' + import { PluginTags } from './components' -import { Tooltip } from 'lib/lemon-ui/Tooltip' export function AppView({ plugin, diff --git a/frontend/src/scenes/plugins/tabs/apps/AppsManagementTab.tsx b/frontend/src/scenes/plugins/tabs/apps/AppsManagementTab.tsx index ea6dc22ccae73..06125e06f06fa 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AppsManagementTab.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AppsManagementTab.tsx @@ -2,15 +2,17 @@ import { LemonButton, LemonDivider } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { IconCloudDownload, IconRefresh } from 'lib/lemon-ui/icons' import { useMemo } from 'react' -import { PluginsSearch } from 'scenes/plugins/PluginsSearch' import { canGloballyManagePlugins } from 'scenes/plugins/access' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginsSearch } from 'scenes/plugins/PluginsSearch' +import { PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' import { userLogic } from 'scenes/userLogic' + +import { PluginType } from '~/types' + import { AdvancedInstallModal } from './AdvancedInstallModal' -import { AppsTable } from './AppsTable' import { AppManagementView } from './AppManagementView' -import { PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' -import { PluginType } from '~/types' +import { AppsTable } from './AppsTable' export function AppsManagementTab(): JSX.Element { const { user } = useValues(userLogic) diff --git a/frontend/src/scenes/plugins/tabs/apps/AppsTab.tsx b/frontend/src/scenes/plugins/tabs/apps/AppsTab.tsx index a02ad02f27fef..384d8d697d63b 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AppsTab.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AppsTab.tsx @@ -1,14 +1,16 @@ import { useValues } from 'kea' -import { PluginsSearch } from 'scenes/plugins/PluginsSearch' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginDrawer } from 'scenes/plugins/edit/PluginDrawer' -import { BatchExportsAlternativeWarning } from './components' -import { InstalledAppsReorderModal } from './InstalledAppsReorderModal' -import { AppsTable } from './AppsTable' -import { AppView } from './AppView' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginsSearch } from 'scenes/plugins/PluginsSearch' import { PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' + import { PluginType } from '~/types' +import { AppsTable } from './AppsTable' +import { AppView } from './AppView' +import { BatchExportsAlternativeWarning } from './components' +import { InstalledAppsReorderModal } from './InstalledAppsReorderModal' + export function AppsTab(): JSX.Element { const { sortableEnabledPlugins, unsortableEnabledPlugins, filteredDisabledPlugins, loading } = useValues(pluginsLogic) diff --git a/frontend/src/scenes/plugins/tabs/apps/AppsTable.tsx b/frontend/src/scenes/plugins/tabs/apps/AppsTable.tsx index 5fb4af0d52ac7..477ed810eab59 100644 --- a/frontend/src/scenes/plugins/tabs/apps/AppsTable.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/AppsTable.tsx @@ -1,9 +1,10 @@ -import { LemonTable, LemonButton } from '@posthog/lemon-ui' +import { LemonButton, LemonTable } from '@posthog/lemon-ui' import { useValues } from 'kea' import { IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' import { useState } from 'react' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { PluginRepositoryEntry, PluginTypeWithConfig } from 'scenes/plugins/types' + import { PluginType } from '~/types' export function AppsTable({ diff --git a/frontend/src/scenes/plugins/tabs/apps/InstalledAppsReorderModal.tsx b/frontend/src/scenes/plugins/tabs/apps/InstalledAppsReorderModal.tsx index 382a157bbbf6f..612aaa0071e8a 100644 --- a/frontend/src/scenes/plugins/tabs/apps/InstalledAppsReorderModal.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/InstalledAppsReorderModal.tsx @@ -1,13 +1,13 @@ -import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { useValues, useActions } from 'kea' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { LemonBadge, LemonButton } from '@posthog/lemon-ui' -import { PluginTypeWithConfig } from 'scenes/plugins/types' -import { PluginImage } from 'scenes/plugins/plugin/PluginImage' -import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' import { DndContext, DragEndEvent } from '@dnd-kit/core' import { restrictToParentElement, restrictToVerticalAxis } from '@dnd-kit/modifiers' +import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' +import { LemonBadge, LemonButton } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { PluginImage } from 'scenes/plugins/plugin/PluginImage' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginTypeWithConfig } from 'scenes/plugins/types' const MinimalAppView = ({ plugin, order }: { plugin: PluginTypeWithConfig; order: number }): JSX.Element => { const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id: plugin.id }) diff --git a/frontend/src/scenes/plugins/tabs/apps/components.tsx b/frontend/src/scenes/plugins/tabs/apps/components.tsx index aa0892cf9e0db..621b3d2bfaa95 100644 --- a/frontend/src/scenes/plugins/tabs/apps/components.tsx +++ b/frontend/src/scenes/plugins/tabs/apps/components.tsx @@ -1,15 +1,16 @@ -import { PluginType } from '~/types' import { LemonTag, Link } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { PluginRepositoryEntry, PluginTab } from 'scenes/plugins/types' import { useValues } from 'kea' -import { pluginsLogic } from 'scenes/plugins/pluginsLogic' -import { organizationLogic } from 'scenes/organizationLogic' import { PluginsAccessLevel } from 'lib/constants' -import { copyToClipboard } from 'lib/utils/copyToClipboard' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { organizationLogic } from 'scenes/organizationLogic' +import { pluginsLogic } from 'scenes/plugins/pluginsLogic' +import { PluginRepositoryEntry, PluginTab } from 'scenes/plugins/types' import { urls } from 'scenes/urls' +import { PluginType } from '~/types' + export function RepositoryTag({ plugin }: { plugin: PluginType | PluginRepositoryEntry }): JSX.Element | null { const { pluginUrlToMaintainer } = useValues(pluginsLogic) diff --git a/frontend/src/scenes/products/Products.tsx b/frontend/src/scenes/products/Products.tsx index cba24e76c8a36..eed399afba9a7 100644 --- a/frontend/src/scenes/products/Products.tsx +++ b/frontend/src/scenes/products/Products.tsx @@ -1,16 +1,19 @@ +import * as Icons from '@posthog/icons' import { LemonButton } from '@posthog/lemon-ui' -import { SceneExport } from 'scenes/sceneTypes' -import { BillingProductV2Type, ProductKey } from '~/types' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' -import { urls } from 'scenes/urls' -import { billingLogic } from 'scenes/billing/billingLogic' -import { Spinner } from 'lib/lemon-ui/Spinner' -import { LemonCard } from 'lib/lemon-ui/LemonCard/LemonCard' import { router } from 'kea-router' +import { LemonCard } from 'lib/lemon-ui/LemonCard/LemonCard' +import { Spinner } from 'lib/lemon-ui/Spinner' +import { billingLogic } from 'scenes/billing/billingLogic' import { getProductUri } from 'scenes/onboarding/onboardingLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { BillingProductV2Type, ProductKey } from '~/types' + import { productsLogic } from './productsLogic' -import * as Icons from '@posthog/icons' export const scene: SceneExport = { component: Products, @@ -27,6 +30,7 @@ function OnboardingCompletedButton({ productKey: ProductKey }): JSX.Element { const { onSelectProduct } = useActions(productsLogic) + return ( <> @@ -94,7 +98,7 @@ export function ProductCard({ return (
    @@ -102,11 +106,11 @@ export function ProductCard({
    {getProductIcon(product.icon_key, 'text-2xl')}
    -
    +

    {product.name}

    {product.description}

    -
    +
    {onboardingCompleted ? ( +

    Pick your {isFirstProduct ? 'first' : 'next'} product.

    diff --git a/frontend/src/scenes/products/productsLogic.tsx b/frontend/src/scenes/products/productsLogic.tsx index 29c2678b63fe7..313e26ea70a1c 100644 --- a/frontend/src/scenes/products/productsLogic.tsx +++ b/frontend/src/scenes/products/productsLogic.tsx @@ -1,10 +1,11 @@ -import { kea, path, actions, listeners, connect } from 'kea' +import { actions, connect, kea, listeners, path } from 'kea' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { onboardingLogic } from 'scenes/onboarding/onboardingLogic' import { teamLogic } from 'scenes/teamLogic' + import { ProductKey } from '~/types' import type { productsLogicType } from './productsLogicType' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { onboardingLogic } from 'scenes/onboarding/onboardingLogic' export const productsLogic = kea([ path(() => ['scenes', 'products', 'productsLogic']), diff --git a/frontend/src/scenes/project-homepage/NewlySeenPersons.tsx b/frontend/src/scenes/project-homepage/NewlySeenPersons.tsx index 64a4181460b29..8df69f2bc4697 100644 --- a/frontend/src/scenes/project-homepage/NewlySeenPersons.tsx +++ b/frontend/src/scenes/project-homepage/NewlySeenPersons.tsx @@ -1,16 +1,17 @@ import './ProjectHomepage.scss' -import { useActions, useValues } from 'kea' -import { dayjs } from 'lib/dayjs' +import { useActions, useValues } from 'kea' import { CompactList } from 'lib/components/CompactList/CompactList' +import { dayjs } from 'lib/dayjs' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { asDisplay } from 'scenes/persons/person-utils' import { urls } from 'scenes/urls' + import { PersonType } from '~/types' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { projectHomepageLogic } from './projectHomepageLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { ProjectHomePageCompactListItem } from './ProjectHomePageCompactListItem' -import { asDisplay } from 'scenes/persons/person-utils' +import { projectHomepageLogic } from './projectHomepageLogic' function PersonRow({ person }: { person: PersonType }): JSX.Element { const { reportPersonOpenedFromNewlySeenPersonsList } = useActions(eventUsageLogic) diff --git a/frontend/src/scenes/project-homepage/ProjectHomePageCompactListItem.tsx b/frontend/src/scenes/project-homepage/ProjectHomePageCompactListItem.tsx index c61ac0e2bcc8c..cefd3e82870bc 100644 --- a/frontend/src/scenes/project-homepage/ProjectHomePageCompactListItem.tsx +++ b/frontend/src/scenes/project-homepage/ProjectHomePageCompactListItem.tsx @@ -17,13 +17,13 @@ export function ProjectHomePageCompactListItem({ suffix, }: RecentItemRowProps): JSX.Element { return ( - -

    - {prefix ? {prefix} : null} + +
    + {prefix ? {prefix} : null} -
    -
    {title}
    -
    {subtitle}
    +
    +
    {title}
    +
    {subtitle}
    {suffix ? {suffix} : null} diff --git a/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx b/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx index fb2a439dbee3b..f62507be91ad2 100644 --- a/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx +++ b/frontend/src/scenes/project-homepage/ProjectHomepage.stories.tsx @@ -1,9 +1,10 @@ -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 { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' import { EMPTY_PAGINATED_RESPONSE } from '~/mocks/handlers' const meta: Meta = { diff --git a/frontend/src/scenes/project-homepage/ProjectHomepage.tsx b/frontend/src/scenes/project-homepage/ProjectHomepage.tsx index b26ec70c514d9..ef77e7b65ef86 100644 --- a/frontend/src/scenes/project-homepage/ProjectHomepage.tsx +++ b/frontend/src/scenes/project-homepage/ProjectHomepage.tsx @@ -1,30 +1,33 @@ -import { useRef } from 'react' import './ProjectHomepage.scss' -import { PageHeader } from 'lib/components/PageHeader' -import { Dashboard } from 'scenes/dashboard/Dashboard' + +import { IconHome } from '@posthog/icons' +import { Link } from '@posthog/lemon-ui' +import useSize from '@react-hook/size' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' -import { Scene, SceneExport } from 'scenes/sceneTypes' -import { DashboardPlacement } from '~/types' -import { inviteLogic } from 'scenes/settings/organization/inviteLogic' +import { PageHeader } from 'lib/components/PageHeader' +import { SceneDashboardChoiceModal } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceModal' +import { sceneDashboardChoiceModalLogic } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' +import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' +import { FEATURE_FLAGS } from 'lib/constants' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { IconHome } from '@posthog/icons' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { useRef } from 'react' +import { Dashboard } from 'scenes/dashboard/Dashboard' +import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' import { projectHomepageLogic } from 'scenes/project-homepage/projectHomepageLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { RecentRecordings } from './RecentRecordings' -import { RecentInsights } from './RecentInsights' -import { NewlySeenPersons } from './NewlySeenPersons' -import useSize from '@react-hook/size' import { NewInsightButton } from 'scenes/saved-insights/SavedInsights' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { Link } from '@posthog/lemon-ui' +import { Scene, SceneExport } from 'scenes/sceneTypes' +import { inviteLogic } from 'scenes/settings/organization/inviteLogic' +import { teamLogic } from 'scenes/teamLogic' import { urls } from 'scenes/urls' -import { dashboardLogic } from 'scenes/dashboard/dashboardLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { sceneDashboardChoiceModalLogic } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' -import { SceneDashboardChoiceModal } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceModal' -import { SceneDashboardChoiceRequired } from 'lib/components/SceneDashboardChoice/SceneDashboardChoiceRequired' + +import { DashboardPlacement } from '~/types' + +import { NewlySeenPersons } from './NewlySeenPersons' +import { RecentInsights } from './RecentInsights' +import { RecentRecordings } from './RecentRecordings' export function ProjectHomepage(): JSX.Element { const { dashboardLogicProps } = useValues(projectHomepageLogic) diff --git a/frontend/src/scenes/project-homepage/RecentInsights.tsx b/frontend/src/scenes/project-homepage/RecentInsights.tsx index 40c5efb4fdba2..c41c6d8b87aab 100644 --- a/frontend/src/scenes/project-homepage/RecentInsights.tsx +++ b/frontend/src/scenes/project-homepage/RecentInsights.tsx @@ -1,16 +1,17 @@ -import { useEffect } from 'react' import './ProjectHomepage.scss' -import { useActions, useValues } from 'kea' -import { dayjs } from 'lib/dayjs' +import { useActions, useValues } from 'kea' import { CompactList } from 'lib/components/CompactList/CompactList' +import { dayjs } from 'lib/dayjs' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { useEffect } from 'react' +import { InsightIcon } from 'scenes/saved-insights/SavedInsights' import { urls } from 'scenes/urls' + import { InsightModel } from '~/types' -import { InsightIcon } from 'scenes/saved-insights/SavedInsights' -import { projectHomepageLogic } from './projectHomepageLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { ProjectHomePageCompactListItem } from './ProjectHomePageCompactListItem' +import { projectHomepageLogic } from './projectHomepageLogic' interface InsightRowProps { insight: InsightModel diff --git a/frontend/src/scenes/project-homepage/RecentRecordings.tsx b/frontend/src/scenes/project-homepage/RecentRecordings.tsx index d8b178148f6ab..e6786752d949d 100644 --- a/frontend/src/scenes/project-homepage/RecentRecordings.tsx +++ b/frontend/src/scenes/project-homepage/RecentRecordings.tsx @@ -1,20 +1,22 @@ -import { dayjs } from 'lib/dayjs' -import { useActions, useValues } from 'kea' - import './ProjectHomepage.scss' + +import { useActions, useValues } from 'kea' import { CompactList } from 'lib/components/CompactList/CompactList' +import { dayjs } from 'lib/dayjs' +import { IconPlayCircle } from 'lib/lemon-ui/icons' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { sessionRecordingsPlaylistLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { urls } from 'scenes/urls' -import { SessionRecordingType } from '~/types' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { humanFriendlyDuration } from 'lib/utils' -import { IconPlayCircle } from 'lib/lemon-ui/icons' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { asDisplay } from 'scenes/persons/person-utils' import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' -import { teamLogic } from 'scenes/teamLogic' import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic' +import { sessionRecordingsPlaylistLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + +import { SessionRecordingType } from '~/types' + import { ProjectHomePageCompactListItem } from './ProjectHomePageCompactListItem' -import { asDisplay } from 'scenes/persons/person-utils' interface RecordingRowProps { recording: SessionRecordingType diff --git a/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts b/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts index 7653bb08aa64e..0818d735c1536 100644 --- a/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts +++ b/frontend/src/scenes/project-homepage/projectHomepageLogic.test.ts @@ -1,9 +1,11 @@ import { expectLogic } from 'kea-test-utils' + import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' import { DashboardType } from '~/types' -import { projectHomepageLogic } from './projectHomepageLogic' + import _dashboardJson from '../dashboard/__mocks__/dashboard.json' +import { projectHomepageLogic } from './projectHomepageLogic' const dashboardJson = _dashboardJson as any as DashboardType diff --git a/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx b/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx index 1aa1402bd372a..681e721705393 100644 --- a/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx +++ b/frontend/src/scenes/project-homepage/projectHomepageLogic.tsx @@ -1,11 +1,12 @@ import { afterMount, connect, kea, path, selectors } from 'kea' - -import type { projectHomepageLogicType } from './projectHomepageLogicType' -import { teamLogic } from 'scenes/teamLogic' +import { loaders } from 'kea-loaders' +import api from 'lib/api' import { DashboardLogicProps } from 'scenes/dashboard/dashboardLogic' +import { teamLogic } from 'scenes/teamLogic' + import { DashboardPlacement, InsightModel, PersonType } from '~/types' -import api from 'lib/api' -import { loaders } from 'kea-loaders' + +import type { projectHomepageLogicType } from './projectHomepageLogicType' export const projectHomepageLogic = kea([ path(['scenes', 'project-homepage', 'projectHomepageLogic']), diff --git a/frontend/src/scenes/project/Create/index.tsx b/frontend/src/scenes/project/Create/index.tsx index 542aa84e527a3..c6d42c631bb30 100644 --- a/frontend/src/scenes/project/Create/index.tsx +++ b/frontend/src/scenes/project/Create/index.tsx @@ -1,9 +1,10 @@ -import { CreateProjectModal } from '../CreateProjectModal' -import { SceneExport } from 'scenes/sceneTypes' -import { teamLogic } from 'scenes/teamLogic' import { useValues } from 'kea' -import { organizationLogic } from 'scenes/organizationLogic' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { organizationLogic } from 'scenes/organizationLogic' +import { SceneExport } from 'scenes/sceneTypes' +import { teamLogic } from 'scenes/teamLogic' + +import { CreateProjectModal } from '../CreateProjectModal' export const scene: SceneExport = { component: ProjectCreate, diff --git a/frontend/src/scenes/project/CreateProjectModal.tsx b/frontend/src/scenes/project/CreateProjectModal.tsx index 878a12f7c0a05..d16abb142f031 100644 --- a/frontend/src/scenes/project/CreateProjectModal.tsx +++ b/frontend/src/scenes/project/CreateProjectModal.tsx @@ -4,6 +4,7 @@ import { PureField } from 'lib/forms/Field' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { useState } from 'react' import { teamLogic } from 'scenes/teamLogic' + import { organizationLogic } from '../organizationLogic' export function CreateProjectModal({ diff --git a/frontend/src/scenes/retention/RetentionContainer.tsx b/frontend/src/scenes/retention/RetentionContainer.tsx index ed99511149e2b..15207bdb39a63 100644 --- a/frontend/src/scenes/retention/RetentionContainer.tsx +++ b/frontend/src/scenes/retention/RetentionContainer.tsx @@ -1,11 +1,13 @@ -import { RetentionLineGraph } from './RetentionLineGraph' -import { RetentionTable } from './RetentionTable' import { LemonDivider } from '@posthog/lemon-ui' -import { RetentionModal } from './RetentionModal' -import { QueryContext } from '~/queries/types' + import { VizSpecificOptions } from '~/queries/schema' +import { QueryContext } from '~/queries/types' import { InsightType } from '~/types' +import { RetentionLineGraph } from './RetentionLineGraph' +import { RetentionModal } from './RetentionModal' +import { RetentionTable } from './RetentionTable' + export function RetentionContainer({ inCardView, inSharedMode, diff --git a/frontend/src/scenes/retention/RetentionLineGraph.tsx b/frontend/src/scenes/retention/RetentionLineGraph.tsx index c241d7cefc2da..f9ddce22272e6 100644 --- a/frontend/src/scenes/retention/RetentionLineGraph.tsx +++ b/frontend/src/scenes/retention/RetentionLineGraph.tsx @@ -1,14 +1,14 @@ import { useActions, useValues } from 'kea' - +import { roundToDecimal } from 'lib/utils' import { insightLogic } from 'scenes/insights/insightLogic' -import { retentionLineGraphLogic } from './retentionLineGraphLogic' -import { retentionModalLogic } from './retentionModalLogic' -import { GraphType, GraphDataset } from '~/types' -import { roundToDecimal } from 'lib/utils' -import { LineGraph } from '../insights/views/LineGraph/LineGraph' -import { InsightEmptyState } from '../insights/EmptyStates' import { TrendsFilter } from '~/queries/schema' +import { GraphDataset, GraphType } from '~/types' + +import { InsightEmptyState } from '../insights/EmptyStates' +import { LineGraph } from '../insights/views/LineGraph/LineGraph' +import { retentionLineGraphLogic } from './retentionLineGraphLogic' +import { retentionModalLogic } from './retentionModalLogic' interface RetentionLineGraphProps { inSharedMode?: boolean diff --git a/frontend/src/scenes/retention/RetentionModal.tsx b/frontend/src/scenes/retention/RetentionModal.tsx index f1d8f7152a995..e3f364621c592 100644 --- a/frontend/src/scenes/retention/RetentionModal.tsx +++ b/frontend/src/scenes/retention/RetentionModal.tsx @@ -1,21 +1,24 @@ -import { capitalizeFirstLetter, isGroupType, percentage } from 'lib/utils' -import { RetentionTableAppearanceType } from 'scenes/retention/types' -import { dayjs } from 'lib/dayjs' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import './RetentionTable.scss' -import { urls } from 'scenes/urls' -import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' + import { LemonButton, LemonModal } from '@posthog/lemon-ui' -import { triggerExport } from 'lib/components/ExportButton/exporter' -import { ExporterFormat } from '~/types' import clsx from 'clsx' -import { MissingPersonsAlert } from 'scenes/trends/persons-modal/PersonsModal' import { useActions, useValues } from 'kea' +import { triggerExport } from 'lib/components/ExportButton/exporter' +import { dayjs } from 'lib/dayjs' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { capitalizeFirstLetter, isGroupType, percentage } from 'lib/utils' import { insightLogic } from 'scenes/insights/insightLogic' +import { groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { asDisplay } from 'scenes/persons/person-utils' +import { RetentionTableAppearanceType } from 'scenes/retention/types' +import { MissingPersonsAlert } from 'scenes/trends/persons-modal/PersonsModal' +import { urls } from 'scenes/urls' + +import { ExporterFormat } from '~/types' + import { retentionLogic } from './retentionLogic' -import { retentionPeopleLogic } from './retentionPeopleLogic' import { retentionModalLogic } from './retentionModalLogic' -import { asDisplay } from 'scenes/persons/person-utils' +import { retentionPeopleLogic } from './retentionPeopleLogic' export function RetentionModal(): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/retention/RetentionTable.tsx b/frontend/src/scenes/retention/RetentionTable.tsx index 967997abe6245..9aaec1a08c916 100644 --- a/frontend/src/scenes/retention/RetentionTable.tsx +++ b/frontend/src/scenes/retention/RetentionTable.tsx @@ -1,13 +1,13 @@ -import { useActions, useValues } from 'kea' -import clsx from 'clsx' +import './RetentionTable.scss' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { BRAND_BLUE_HSL, gradateColor } from 'lib/colors' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { insightLogic } from 'scenes/insights/insightLogic' -import { retentionTableLogic } from './retentionTableLogic' -import { retentionModalLogic } from './retentionModalLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import './RetentionTable.scss' -import { BRAND_BLUE_HSL, gradateColor } from 'lib/colors' +import { retentionModalLogic } from './retentionModalLogic' +import { retentionTableLogic } from './retentionTableLogic' export function RetentionTable({ inCardView = false }: { inCardView?: boolean }): JSX.Element | null { const { insightProps } = useValues(insightLogic) diff --git a/frontend/src/scenes/retention/constants.ts b/frontend/src/scenes/retention/constants.ts index 8137c07d8447b..a33b7fb00d6a8 100644 --- a/frontend/src/scenes/retention/constants.ts +++ b/frontend/src/scenes/retention/constants.ts @@ -1,5 +1,6 @@ -import { OpUnitType } from 'lib/dayjs' import { RETENTION_FIRST_TIME, RETENTION_RECURRING } from 'lib/constants' +import { OpUnitType } from 'lib/dayjs' + import { RetentionPeriod } from '~/types' export const dateOptions: RetentionPeriod[] = [ diff --git a/frontend/src/scenes/retention/retentionLineGraphLogic.test.ts b/frontend/src/scenes/retention/retentionLineGraphLogic.test.ts index c3bfb3a623306..fb565e67a97a0 100644 --- a/frontend/src/scenes/retention/retentionLineGraphLogic.test.ts +++ b/frontend/src/scenes/retention/retentionLineGraphLogic.test.ts @@ -1,9 +1,10 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { retentionLineGraphLogic } from 'scenes/retention/retentionLineGraphLogic' import { insightLogic } from 'scenes/insights/insightLogic' -import { InsightShortId, InsightType, RetentionFilterType } from '~/types' +import { retentionLineGraphLogic } from 'scenes/retention/retentionLineGraphLogic' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { InsightShortId, InsightType, RetentionFilterType } from '~/types' const Insight123 = '123' as InsightShortId const result = [ diff --git a/frontend/src/scenes/retention/retentionLineGraphLogic.ts b/frontend/src/scenes/retention/retentionLineGraphLogic.ts index 656307fb41d38..ba460e3a88ea7 100644 --- a/frontend/src/scenes/retention/retentionLineGraphLogic.ts +++ b/frontend/src/scenes/retention/retentionLineGraphLogic.ts @@ -1,15 +1,15 @@ +import { connect, kea, key, path, props, selectors } from 'kea' import { dayjs, QUnitType } from 'lib/dayjs' -import { kea, props, key, path, connect, selectors } from 'kea' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { RetentionTrendPayload } from 'scenes/retention/types' -import { InsightLogicProps, RetentionPeriod } from '~/types' -import { dateOptionToTimeIntervalMap } from './constants' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { retentionLogic } from './retentionLogic' +import { isLifecycleQuery, isStickinessQuery } from '~/queries/utils' +import { InsightLogicProps, RetentionPeriod } from '~/types' +import { dateOptionToTimeIntervalMap } from './constants' import type { retentionLineGraphLogicType } from './retentionLineGraphLogicType' -import { isLifecycleQuery, isStickinessQuery } from '~/queries/utils' +import { retentionLogic } from './retentionLogic' const DEFAULT_RETENTION_LOGIC_KEY = 'default_retention_key' diff --git a/frontend/src/scenes/retention/retentionLogic.ts b/frontend/src/scenes/retention/retentionLogic.ts index bf97e36dfdbb6..006d41843efcb 100644 --- a/frontend/src/scenes/retention/retentionLogic.ts +++ b/frontend/src/scenes/retention/retentionLogic.ts @@ -1,7 +1,8 @@ -import { kea, props, key, path, connect, selectors } from 'kea' +import { connect, kea, key, path, props, selectors } from 'kea' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { RetentionTablePayload } from 'scenes/retention/types' + import { isRetentionQuery } from '~/queries/utils' import { InsightLogicProps } from '~/types' diff --git a/frontend/src/scenes/retention/retentionModalLogic.ts b/frontend/src/scenes/retention/retentionModalLogic.ts index 5d5c4043a8112..47b1eec17c03d 100644 --- a/frontend/src/scenes/retention/retentionModalLogic.ts +++ b/frontend/src/scenes/retention/retentionModalLogic.ts @@ -1,13 +1,13 @@ -import { kea, props, key, path, connect, actions, reducers, selectors, listeners } from 'kea' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' -import { Noun, groupsModel } from '~/models/groupsModel' -import { InsightLogicProps } from '~/types' -import { retentionPeopleLogic } from './retentionPeopleLogic' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { groupsModel, Noun } from '~/models/groupsModel' +import { isLifecycleQuery, isStickinessQuery } from '~/queries/utils' +import { InsightLogicProps } from '~/types' import type { retentionModalLogicType } from './retentionModalLogicType' -import { isLifecycleQuery, isStickinessQuery } from '~/queries/utils' +import { retentionPeopleLogic } from './retentionPeopleLogic' const DEFAULT_RETENTION_LOGIC_KEY = 'default_retention_key' diff --git a/frontend/src/scenes/retention/retentionPeopleLogic.ts b/frontend/src/scenes/retention/retentionPeopleLogic.ts index 72024de2a8589..2cc3b73e9a7f1 100644 --- a/frontend/src/scenes/retention/retentionPeopleLogic.ts +++ b/frontend/src/scenes/retention/retentionPeopleLogic.ts @@ -1,13 +1,13 @@ +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, props, key, path, connect, actions, reducers, selectors, listeners } from 'kea' import api from 'lib/api' import { toParams } from 'lib/utils' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { RetentionTablePeoplePayload } from 'scenes/retention/types' -import { InsightLogicProps } from '~/types' -import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' +import { InsightLogicProps } from '~/types' import type { retentionPeopleLogicType } from './retentionPeopleLogicType' diff --git a/frontend/src/scenes/retention/retentionTableLogic.test.ts b/frontend/src/scenes/retention/retentionTableLogic.test.ts index 707c36870fe84..78f4baaa107cc 100644 --- a/frontend/src/scenes/retention/retentionTableLogic.test.ts +++ b/frontend/src/scenes/retention/retentionTableLogic.test.ts @@ -1,9 +1,10 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { retentionTableLogic } from 'scenes/retention/retentionTableLogic' import { insightLogic } from 'scenes/insights/insightLogic' -import { InsightShortId, InsightType, RetentionFilterType } from '~/types' +import { retentionTableLogic } from 'scenes/retention/retentionTableLogic' + import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { InsightShortId, InsightType, RetentionFilterType } from '~/types' const Insight123 = '123' as InsightShortId const result = [ diff --git a/frontend/src/scenes/retention/retentionTableLogic.ts b/frontend/src/scenes/retention/retentionTableLogic.ts index aa495e9d3a68c..130145bc4e0e3 100644 --- a/frontend/src/scenes/retention/retentionTableLogic.ts +++ b/frontend/src/scenes/retention/retentionTableLogic.ts @@ -1,12 +1,12 @@ +import { connect, kea, key, path, props, selectors } from 'kea' import { dayjs } from 'lib/dayjs' -import { kea, props, key, path, connect, selectors } from 'kea' import { range } from 'lib/utils' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + import { InsightLogicProps, InsightType } from '~/types' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { retentionLogic } from './retentionLogic' - import type { retentionTableLogicType } from './retentionTableLogicType' const DEFAULT_RETENTION_LOGIC_KEY = 'default_retention_key' diff --git a/frontend/src/scenes/saved-insights/SavedInsights.scss b/frontend/src/scenes/saved-insights/SavedInsights.scss index 84e02c0b92476..a50d141741fc7 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.scss +++ b/frontend/src/scenes/saved-insights/SavedInsights.scss @@ -5,9 +5,14 @@ padding-bottom: 0.75rem !important; padding-top: 0.75rem !important; - &.icon-column .LemonSkeleton { - height: 2rem; - width: 2rem; + &.icon-column { + font-size: 1.5rem; + color: var(--muted); + + .LemonSkeleton { + height: 2rem; + width: 2rem; + } } } diff --git a/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx b/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx index 3b4ee1eb9a163..4e9f15449536f 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx +++ b/frontend/src/scenes/saved-insights/SavedInsights.stories.tsx @@ -1,16 +1,15 @@ import { Meta, Story } from '@storybook/react' - +import { router } from 'kea-router' +import { useEffect } from 'react' import { App } from 'scenes/App' -import insightsJson from './__mocks__/insights.json' -import { useEffect } from 'react' -import { router } from 'kea-router' import { mswDecorator, useStorybookMocks } from '~/mocks/browser' +import { EMPTY_PAGINATED_RESPONSE, toPaginatedResponse } from '~/mocks/handlers' +import funnelTopToBottom from '../../mocks/fixtures/api/projects/team_id/insights/funnelTopToBottom.json' import trendsBarBreakdown from '../../mocks/fixtures/api/projects/team_id/insights/trendsBarBreakdown.json' import trendsPieBreakdown from '../../mocks/fixtures/api/projects/team_id/insights/trendsPieBreakdown.json' -import funnelTopToBottom from '../../mocks/fixtures/api/projects/team_id/insights/funnelTopToBottom.json' -import { EMPTY_PAGINATED_RESPONSE, toPaginatedResponse } from '~/mocks/handlers' +import insightsJson from './__mocks__/insights.json' const insights = [trendsBarBreakdown, trendsPieBreakdown, funnelTopToBottom] diff --git a/frontend/src/scenes/saved-insights/SavedInsights.tsx b/frontend/src/scenes/saved-insights/SavedInsights.tsx index 4b9052ff38c36..8b5efc9cfe5f5 100644 --- a/frontend/src/scenes/saved-insights/SavedInsights.tsx +++ b/frontend/src/scenes/saved-insights/SavedInsights.tsx @@ -1,24 +1,26 @@ -import { useActions, useValues } from 'kea' -import { Link } from 'lib/lemon-ui/Link' -import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { InsightModel, InsightType, LayoutView, SavedInsightsTabs } from '~/types' -import { INSIGHTS_PER_PAGE, savedInsightsLogic } from './savedInsightsLogic' import './SavedInsights.scss' -import { organizationLogic } from 'scenes/organizationLogic' -import { PageHeader } from 'lib/components/PageHeader' -import { SavedInsightsEmptyState } from 'scenes/insights/EmptyStates' -import { teamLogic } from '../teamLogic' + import { IconBrackets, IconFunnels, IconHogQL, IconLifecycle, IconRetention, + IconStar, + IconStarFilled, IconStickiness, IconTrends, IconUserPaths, } from '@posthog/icons' +import { LemonSelectOptions } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' +import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' +import { InsightCard } from 'lib/components/Cards/InsightCard' +import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { PageHeader } from 'lib/components/PageHeader' +import { TZLabel } from 'lib/components/TZLabel' +import { FEATURE_FLAGS } from 'lib/constants' import { IconAction, IconBarChart, @@ -29,38 +31,38 @@ import { IconPerson, IconPlusMini, IconSelectEvents, - IconStarFilled, - IconStarOutline, IconTableChart, } from 'lib/lemon-ui/icons' -import { SceneExport } from 'scenes/sceneTypes' -import { TZLabel } from 'lib/components/TZLabel' -import { urls } from 'scenes/urls' -import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonButton, LemonButtonWithSideAction, LemonButtonWithSideActionProps } 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 { LemonSegmentedButton } from 'lib/lemon-ui/LemonSegmentedButton' +import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' -import { LemonButton, LemonButtonWithSideAction, LemonButtonWithSideActionProps } from 'lib/lemon-ui/LemonButton' -import { InsightCard } from 'lib/components/Cards/InsightCard' - -import { groupsModel } from '~/models/groupsModel' -import { cohortsModel } from '~/models/cohortsModel' -import { mathsLogic } from 'scenes/trends/mathsLogic' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Link } from 'lib/lemon-ui/Link' import { PaginationControl, usePagination } from 'lib/lemon-ui/PaginationControl' -import { ActivityScope } from 'lib/components/ActivityLog/humanizeActivity' -import { ActivityLog } from 'lib/components/ActivityLog/ActivityLog' -import { LemonSelectOptions } from '@posthog/lemon-ui' import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' +import { SavedInsightsEmptyState } from 'scenes/insights/EmptyStates' +import { summarizeInsight } from 'scenes/insights/summarizeInsight' +import { organizationLogic } from 'scenes/organizationLogic' +import { overlayForNewInsightMenu } from 'scenes/saved-insights/newInsightsMenu' import { SavedInsightsFilters } from 'scenes/saved-insights/SavedInsightsFilters' +import { SceneExport } from 'scenes/sceneTypes' +import { mathsLogic } from 'scenes/trends/mathsLogic' +import { urls } from 'scenes/urls' + +import { cohortsModel } from '~/models/cohortsModel' +import { groupsModel } from '~/models/groupsModel' import { NodeKind } from '~/queries/schema' -import { LemonSegmentedButton } from 'lib/lemon-ui/LemonSegmentedButton' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' import { isInsightVizNode } from '~/queries/utils' -import { overlayForNewInsightMenu } from 'scenes/saved-insights/newInsightsMenu' -import { summarizeInsight } from 'scenes/insights/summarizeInsight' -import { LemonMarkdown } from 'lib/lemon-ui/LemonMarkdown' +import { InsightModel, InsightType, LayoutView, SavedInsightsTabs } from '~/types' + +import { teamLogic } from '../teamLogic' +import { INSIGHTS_PER_PAGE, savedInsightsLogic } from './savedInsightsLogic' interface NewInsightButtonProps { dataAttr: string @@ -304,7 +306,7 @@ export function InsightIcon({ insight }: { insight: InsightModel }): JSX.Element } const insightMetadata = INSIGHT_TYPES_METADATA[insightType] if (insightMetadata && insightMetadata.icon) { - return + return } return null } @@ -429,10 +431,10 @@ export function SavedInsights(): JSX.Element { insight.favorited ? ( ) : ( - + ) } - tooltip={`${insight.favorited ? 'Add to' : 'Remove from'} favorite insights`} + tooltip={`${insight.favorited ? 'Remove from' : 'Add to'} favorite insights`} /> {hasDashboardCollaboration && insight.description && ( diff --git a/frontend/src/scenes/saved-insights/SavedInsightsFilters.tsx b/frontend/src/scenes/saved-insights/SavedInsightsFilters.tsx index 33369ddf00218..e1e9e66aa111c 100644 --- a/frontend/src/scenes/saved-insights/SavedInsightsFilters.tsx +++ b/frontend/src/scenes/saved-insights/SavedInsightsFilters.tsx @@ -1,13 +1,14 @@ -import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { IconCalendar } from '@posthog/icons' +import { useActions, useValues } from 'kea' import { DateFilter } from 'lib/components/DateFilter/DateFilter' -import { SavedInsightsTabs } from '~/types' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { LemonSelect } from 'lib/lemon-ui/LemonSelect' +import { membersLogic } from 'scenes/organization/membersLogic' import { INSIGHT_TYPE_OPTIONS } from 'scenes/saved-insights/SavedInsights' -import { useActions, useValues } from 'kea' -import { dashboardsModel } from '~/models/dashboardsModel' import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' -import { membersLogic } from 'scenes/organization/membersLogic' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { IconCalendar } from '@posthog/icons' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { SavedInsightsTabs } from '~/types' export function SavedInsightsFilters(): JSX.Element { const { nameSortedDashboards } = useValues(dashboardsModel) diff --git a/frontend/src/scenes/saved-insights/activityDescriptions.tsx b/frontend/src/scenes/saved-insights/activityDescriptions.tsx index ab67fb7972184..1ab0fb0e218c2 100644 --- a/frontend/src/scenes/saved-insights/activityDescriptions.tsx +++ b/frontend/src/scenes/saved-insights/activityDescriptions.tsx @@ -1,3 +1,6 @@ +import '../../lib/components/Cards/InsightCard/InsightCard.scss' + +import { captureException } from '@sentry/react' import { ActivityChange, ActivityLogItem, @@ -6,18 +9,17 @@ import { detectBoolean, HumanizedChange, } from 'lib/components/ActivityLog/humanizeActivity' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' -import { FilterType, InsightModel, InsightShortId } from '~/types' +import { SentenceList } from 'lib/components/ActivityLog/SentenceList' import { BreakdownSummary, FiltersSummary, QuerySummary } from 'lib/components/Cards/InsightCard/InsightDetails' -import '../../lib/components/Cards/InsightCard/InsightCard.scss' import { ObjectTags } from 'lib/components/ObjectTags/ObjectTags' +import { Link } from 'lib/lemon-ui/Link' import { areObjectValuesEmpty, pluralize } from 'lib/utils' -import { SentenceList } from 'lib/components/ActivityLog/SentenceList' +import { urls } from 'scenes/urls' + import { queryNodeToFilter } from '~/queries/nodes/InsightQuery/utils/queryNodeToFilter' import { InsightQueryNode, QuerySchema } from '~/queries/schema' import { isInsightQueryNode } from '~/queries/utils' -import { captureException } from '@sentry/react' +import { FilterType, InsightModel, InsightShortId } from '~/types' const nameOrLinkToInsight = (short_id?: InsightShortId | null, name?: string | null): string | JSX.Element => { const displayName = name || '(empty string)' diff --git a/frontend/src/scenes/saved-insights/newInsightsMenu.tsx b/frontend/src/scenes/saved-insights/newInsightsMenu.tsx index 3e67827c42907..e7dee45af8d99 100644 --- a/frontend/src/scenes/saved-insights/newInsightsMenu.tsx +++ b/frontend/src/scenes/saved-insights/newInsightsMenu.tsx @@ -1,9 +1,10 @@ -import { InsightType } from '~/types' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { INSIGHT_TYPES_METADATA, InsightTypeMetadata } from 'scenes/saved-insights/SavedInsights' import { ReactNode } from 'react' import { insightTypeURL } from 'scenes/insights/utils' +import { INSIGHT_TYPES_METADATA, InsightTypeMetadata } from 'scenes/saved-insights/SavedInsights' + +import { InsightType } from '~/types' function insightTypesForMenu(): [string, InsightTypeMetadata][] { // never show JSON InsightType in the menu @@ -33,7 +34,7 @@ export function overlayForNewInsightMenu(dataAttr: string): ReactNode[] { >
    {listedInsightTypeMetadata.name} - {listedInsightTypeMetadata.description} + {listedInsightTypeMetadata.description}
    ) diff --git a/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts b/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts index b85a15facf92f..37957afac97c9 100644 --- a/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts +++ b/frontend/src/scenes/saved-insights/savedInsightsLogic.test.ts @@ -1,16 +1,18 @@ -import { expectLogic, partial } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { InsightsResult, savedInsightsLogic } from './savedInsightsLogic' -import { InsightModel, InsightType } from '~/types' import { combineUrl, router } from 'kea-router' -import { urls } from 'scenes/urls' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' -import { useMocks } from '~/mocks/jest' +import { expectLogic, partial } from 'kea-test-utils' import api from 'lib/api' import { MOCK_TEAM_ID } from 'lib/api.mock' -import { DuplicateDashboardForm, duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic' import { DeleteDashboardForm, deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic' +import { DuplicateDashboardForm, duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { urls } from 'scenes/urls' + +import { useMocks } from '~/mocks/jest' import { dashboardsModel } from '~/models/dashboardsModel' +import { initKeaTests } from '~/test/init' +import { InsightModel, InsightType } from '~/types' + +import { InsightsResult, savedInsightsLogic } from './savedInsightsLogic' jest.spyOn(api, 'create') diff --git a/frontend/src/scenes/saved-insights/savedInsightsLogic.ts b/frontend/src/scenes/saved-insights/savedInsightsLogic.ts index 349ea9e43e0f7..cb1f40ff676d0 100644 --- a/frontend/src/scenes/saved-insights/savedInsightsLogic.ts +++ b/frontend/src/scenes/saved-insights/savedInsightsLogic.ts @@ -1,22 +1,24 @@ +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners } from 'kea' -import { router, actionToUrl, urlToAction } from 'kea-router' +import { actionToUrl, router, urlToAction } from 'kea-router' import api from 'lib/api' -import { objectDiffShallow, objectsEqual, toParams } from 'lib/utils' -import { InsightModel, LayoutView, SavedInsightsTabs } from '~/types' -import type { savedInsightsLogicType } from './savedInsightsLogicType' import { dayjs } from 'lib/dayjs' -import { insightsModel } from '~/models/insightsModel' -import { teamLogic } from '../teamLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { Sorting } from 'lib/lemon-ui/LemonTable' -import { urls } from 'scenes/urls' import { lemonToast } from 'lib/lemon-ui/lemonToast' import { PaginationManual } from 'lib/lemon-ui/PaginationControl' -import { dashboardsModel } from '~/models/dashboardsModel' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { objectDiffShallow, objectsEqual, toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { deleteDashboardLogic } from 'scenes/dashboard/deleteDashboardLogic' import { duplicateDashboardLogic } from 'scenes/dashboard/duplicateDashboardLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { urls } from 'scenes/urls' + +import { dashboardsModel } from '~/models/dashboardsModel' +import { insightsModel } from '~/models/insightsModel' +import { InsightModel, LayoutView, SavedInsightsTabs } from '~/types' + +import { teamLogic } from '../teamLogic' +import type { savedInsightsLogicType } from './savedInsightsLogicType' export const INSIGHTS_PER_PAGE = 30 diff --git a/frontend/src/scenes/sceneLogic.test.tsx b/frontend/src/scenes/sceneLogic.test.tsx index 478b3047d70ed..0ca7d96dd0e28 100644 --- a/frontend/src/scenes/sceneLogic.test.tsx +++ b/frontend/src/scenes/sceneLogic.test.tsx @@ -1,13 +1,14 @@ -import { sceneLogic } from './sceneLogic' -import { initKeaTests } from '~/test/init' +import { kea, path } from 'kea' +import { router } from 'kea-router' import { expectLogic, partial, truth } from 'kea-test-utils' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { Scene } from 'scenes/sceneTypes' import { teamLogic } from 'scenes/teamLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { router } from 'kea-router' import { urls } from 'scenes/urls' -import { kea, path } from 'kea' +import { initKeaTests } from '~/test/init' + +import { sceneLogic } from './sceneLogic' import type { logicType } from './sceneLogic.testType' export const Component = (): JSX.Element =>
    diff --git a/frontend/src/scenes/sceneLogic.ts b/frontend/src/scenes/sceneLogic.ts index 8e87f1029fd0b..a08bfab9ef878 100644 --- a/frontend/src/scenes/sceneLogic.ts +++ b/frontend/src/scenes/sceneLogic.ts @@ -1,18 +1,20 @@ -import { BuiltLogic, kea, props, path, connect, actions, reducers, selectors, listeners } from 'kea' +import { actions, BuiltLogic, connect, kea, listeners, path, props, reducers, selectors } from 'kea' import { router, urlToAction } from 'kea-router' -import posthog from 'posthog-js' -import type { sceneLogicType } from './sceneLogicType' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { preflightLogic } from './PreflightCheck/preflightLogic' +import posthog from 'posthog-js' +import { emptySceneParams, preloadedScenes, redirects, routes, sceneConfigurations } from 'scenes/scenes' +import { LoadedScene, Params, Scene, SceneConfig, SceneExport, SceneParams } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { AvailableFeature } from '~/types' -import { userLogic } from './userLogic' + +import { appContextLogic } from './appContextLogic' import { handleLoginRedirect } from './authentication/loginLogic' -import { teamLogic } from './teamLogic' -import { urls } from 'scenes/urls' -import { LoadedScene, Params, Scene, SceneConfig, SceneExport, SceneParams } from 'scenes/sceneTypes' -import { emptySceneParams, preloadedScenes, redirects, routes, sceneConfigurations } from 'scenes/scenes' import { organizationLogic } from './organizationLogic' -import { appContextLogic } from './appContextLogic' +import { preflightLogic } from './PreflightCheck/preflightLogic' +import type { sceneLogicType } from './sceneLogicType' +import { teamLogic } from './teamLogic' +import { userLogic } from './userLogic' /** Mapping of some scenes that aren't directly accessible from the sidebar to ones that are - for the sidebar. */ const sceneNavAlias: Partial> = { diff --git a/frontend/src/scenes/scenes.ts b/frontend/src/scenes/scenes.ts index b174df0ff581b..370847c6f4479 100644 --- a/frontend/src/scenes/scenes.ts +++ b/frontend/src/scenes/scenes.ts @@ -1,14 +1,15 @@ +import { combineUrl } from 'kea-router' +import { dayjs } from 'lib/dayjs' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { getDefaultEventsSceneQuery } from 'scenes/events/defaults' import { LoadedScene, Params, Scene, SceneConfig } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { Error404 as Error404Component } from '~/layout/Error404' import { ErrorNetwork as ErrorNetworkComponent } from '~/layout/ErrorNetwork' import { ErrorProjectUnavailable as ErrorProjectUnavailableComponent } from '~/layout/ErrorProjectUnavailable' -import { urls } from 'scenes/urls' -import { InsightShortId, PipelineAppTabs, PipelineTabs, PropertyFilterType, ReplayTabs } from '~/types' -import { combineUrl } from 'kea-router' -import { getDefaultEventsSceneQuery } from 'scenes/events/defaults' import { EventsQuery } from '~/queries/schema' -import { dayjs } from 'lib/dayjs' -import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { InsightShortId, PipelineAppTabs, PipelineTabs, PropertyFilterType, ReplayTabs } from '~/types' export const emptySceneParams = { params: {}, searchParams: {}, hashParams: {} } @@ -118,7 +119,7 @@ export const sceneConfigurations: Partial> = { }, [Scene.Experiments]: { projectBased: true, - name: 'Experiments', + name: 'A/B testing', }, [Scene.Experiment]: { projectBased: true, diff --git a/frontend/src/scenes/session-recordings/SessionRecordings.tsx b/frontend/src/scenes/session-recordings/SessionRecordings.tsx index fa3277a7283e1..a11e6c7c11acf 100644 --- a/frontend/src/scenes/session-recordings/SessionRecordings.tsx +++ b/frontend/src/scenes/session-recordings/SessionRecordings.tsx @@ -1,30 +1,32 @@ -import { PageHeader } from 'lib/components/PageHeader' -import { teamLogic } from 'scenes/teamLogic' -import { useActions, useValues } from 'kea' -import { urls } from 'scenes/urls' -import { SceneExport } from 'scenes/sceneTypes' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonButton } from '@posthog/lemon-ui' -import { AvailableFeature, NotebookNodeType, ReplayTabs } from '~/types' -import { SavedSessionRecordingPlaylists } from './saved-playlists/SavedSessionRecordingPlaylists' -import { humanFriendlyTabName, sessionRecordingsLogic } from './sessionRecordingsLogic' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { IconSettings } from 'lib/lemon-ui/icons' +import { useActions, useValues } from 'kea' import { router } from 'kea-router' -import { SessionRecordingFilePlayback } from './file-playback/SessionRecordingFilePlayback' -import { createPlaylist } from './playlist/playlistUtils' +import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { PageHeader } from 'lib/components/PageHeader' +import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner' import { useAsyncHandler } from 'lib/hooks/useAsyncHandler' +import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { IconSettings } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { sceneLogic } from 'scenes/sceneLogic' -import { savedSessionRecordingPlaylistsLogic } from './saved-playlists/savedSessionRecordingPlaylistsLogic' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { SceneExport } from 'scenes/sceneTypes' import { sessionRecordingsPlaylistLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner' -import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' -import { SessionRecordingsPlaylist } from './playlist/SessionRecordingsPlaylist' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' -import { useFeatureFlag } from 'lib/hooks/useFeatureFlag' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' + import { sidePanelSettingsLogic } from '~/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic' +import { AvailableFeature, NotebookNodeType, ReplayTabs } from '~/types' + +import { SessionRecordingFilePlayback } from './file-playback/SessionRecordingFilePlayback' +import { createPlaylist } from './playlist/playlistUtils' +import { SessionRecordingsPlaylist } from './playlist/SessionRecordingsPlaylist' +import { SavedSessionRecordingPlaylists } from './saved-playlists/SavedSessionRecordingPlaylists' +import { savedSessionRecordingPlaylistsLogic } from './saved-playlists/savedSessionRecordingPlaylistsLogic' +import { humanFriendlyTabName, sessionRecordingsLogic } from './sessionRecordingsLogic' export function SessionsRecordings(): JSX.Element { const { currentTeam } = useValues(teamLogic) diff --git a/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx b/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx index 501351427ee2a..5aea487a76d0b 100644 --- a/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx +++ b/frontend/src/scenes/session-recordings/SessionsRecordings-player-success.stories.tsx @@ -1,23 +1,25 @@ import { Meta } from '@storybook/react' -import recordings from './__mocks__/recordings.json' -import { useEffect } from 'react' -import { mswDecorator } from '~/mocks/browser' import { combineUrl, router } from 'kea-router' -import { urls } from 'scenes/urls' +import { useEffect } from 'react' import { App } from 'scenes/App' -import { snapshotsAsJSONLines } from 'scenes/session-recordings/__mocks__/recording_snapshots' -import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' import recordingEventsJson from 'scenes/session-recordings/__mocks__/recording_events_query' +import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' +import { snapshotsAsJSONLines } from 'scenes/session-recordings/__mocks__/recording_snapshots' +import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' + import recording_playlists from './__mocks__/recording_playlists.json' +import recordings from './__mocks__/recordings.json' const meta: Meta = { title: 'Scenes-App/Recordings', + tags: ['test-skip'], // TODO: Fix the flakey rendering due to player playback parameters: { layout: 'fullscreen', viewMode: 'story', mockDate: '2023-02-01', waitForSelector: '.PlayerFrame__content .replayer-wrapper iframe', - testOptions: { skip: true }, // TODO: Fix the flakey rendering due to player playback }, decorators: [ mswDecorator({ diff --git a/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx b/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx index 657fbccf4bc29..d1df2f5fe537a 100644 --- a/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx +++ b/frontend/src/scenes/session-recordings/SessionsRecordings-playlist-listing.stories.tsx @@ -1,13 +1,15 @@ import { Meta } from '@storybook/react' -import { useEffect } from 'react' -import { mswDecorator } from '~/mocks/browser' import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { useEffect } from 'react' import { App } from 'scenes/App' -import recording_playlists from './__mocks__/recording_playlists.json' -import { ReplayTabs } from '~/types' -import recordings from 'scenes/session-recordings/__mocks__/recordings.json' import recordingEventsJson from 'scenes/session-recordings/__mocks__/recording_events_query' +import recordings from 'scenes/session-recordings/__mocks__/recordings.json' +import { urls } from 'scenes/urls' + +import { mswDecorator } from '~/mocks/browser' +import { ReplayTabs } from '~/types' + +import recording_playlists from './__mocks__/recording_playlists.json' const meta: Meta = { title: 'Scenes-App/Recordings', diff --git a/frontend/src/scenes/session-recordings/detail/SessionRecordingDetail.tsx b/frontend/src/scenes/session-recordings/detail/SessionRecordingDetail.tsx index 721a17c735e9c..6b735227f6376 100644 --- a/frontend/src/scenes/session-recordings/detail/SessionRecordingDetail.tsx +++ b/frontend/src/scenes/session-recordings/detail/SessionRecordingDetail.tsx @@ -1,18 +1,18 @@ +import './SessionRecordingScene.scss' + import { useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' import { PageHeader } from 'lib/components/PageHeader' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' import { SceneExport } from 'scenes/sceneTypes' -import { SessionRecordingPlayer } from 'scenes/session-recordings/player/SessionRecordingPlayer' import { sessionRecordingDetailLogic, SessionRecordingDetailLogicProps, } from 'scenes/session-recordings/detail/sessionRecordingDetailLogic' import { RecordingNotFound } from 'scenes/session-recordings/player/RecordingNotFound' - -import './SessionRecordingScene.scss' +import { SessionRecordingPlayer } from 'scenes/session-recordings/player/SessionRecordingPlayer' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' export const scene: SceneExport = { logic: sessionRecordingDetailLogic, diff --git a/frontend/src/scenes/session-recordings/detail/sessionRecordingDetailLogic.ts b/frontend/src/scenes/session-recordings/detail/sessionRecordingDetailLogic.ts index 58c1e8e23a806..aad8e724401e5 100644 --- a/frontend/src/scenes/session-recordings/detail/sessionRecordingDetailLogic.ts +++ b/frontend/src/scenes/session-recordings/detail/sessionRecordingDetailLogic.ts @@ -1,8 +1,10 @@ -import { kea, props, path, selectors } from 'kea' +import { kea, path, props, selectors } from 'kea' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + import { Breadcrumb, SessionRecordingType } from '~/types' + import type { sessionRecordingDetailLogicType } from './sessionRecordingDetailLogicType' -import { urls } from 'scenes/urls' -import { Scene } from 'scenes/sceneTypes' export interface SessionRecordingDetailLogicProps { id?: SessionRecordingType['id'] diff --git a/frontend/src/scenes/session-recordings/file-playback/SessionRecordingFilePlayback.tsx b/frontend/src/scenes/session-recordings/file-playback/SessionRecordingFilePlayback.tsx index 21f2f6ff83877..0d0f0bd3a5346 100644 --- a/frontend/src/scenes/session-recordings/file-playback/SessionRecordingFilePlayback.tsx +++ b/frontend/src/scenes/session-recordings/file-playback/SessionRecordingFilePlayback.tsx @@ -1,13 +1,15 @@ +import Dragger from 'antd/lib/upload/Dragger' import { useActions, useValues } from 'kea' +import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' import { IconUploadFile } from 'lib/lemon-ui/icons' -import Dragger from 'antd/lib/upload/Dragger' -import { SessionRecordingPlayer } from '../player/SessionRecordingPlayer' -import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { sessionRecordingFilePlaybackLogic } from './sessionRecordingFilePlaybackLogic' +import { SpinnerOverlay } from 'lib/lemon-ui/Spinner/Spinner' import { userLogic } from 'scenes/userLogic' + import { AvailableFeature } from '~/types' -import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' + +import { SessionRecordingPlayer } from '../player/SessionRecordingPlayer' +import { sessionRecordingFilePlaybackLogic } from './sessionRecordingFilePlaybackLogic' export function SessionRecordingFilePlayback(): JSX.Element { const { loadFromFile, resetSessionRecording } = useActions(sessionRecordingFilePlaybackLogic) diff --git a/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.test.ts b/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.test.ts index 91ac5efa25cd9..05b3265f43757 100644 --- a/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.test.ts +++ b/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.test.ts @@ -1,5 +1,7 @@ -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' + +import { initKeaTests } from '~/test/init' + import { sessionRecordingFilePlaybackLogic } from './sessionRecordingFilePlaybackLogic' describe('sessionRecordingFilePlaybackLogic', () => { diff --git a/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.ts b/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.ts index 596692d8ca162..8126bf6a97c35 100644 --- a/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.ts +++ b/frontend/src/scenes/session-recordings/file-playback/sessionRecordingFilePlaybackLogic.ts @@ -1,20 +1,19 @@ +import { lemonToast } from '@posthog/lemon-ui' +import { eventWithTime } from '@rrweb/types' import { BuiltLogic, connect, kea, listeners, path, reducers, selectors } from 'kea' -import { Breadcrumb, PersonType, RecordingSnapshot, ReplayTabs, SessionRecordingType } from '~/types' -import { urls } from 'scenes/urls' import { loaders } from 'kea-loaders' - import { beforeUnload } from 'kea-router' -import { lemonToast } from '@posthog/lemon-ui' - -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { dayjs } from 'lib/dayjs' import { uuid } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb, PersonType, RecordingSnapshot, ReplayTabs, SessionRecordingType } from '~/types' -import type { sessionRecordingFilePlaybackLogicType } from './sessionRecordingFilePlaybackLogicType' -import { eventWithTime } from '@rrweb/types' -import type { sessionRecordingDataLogicType } from '../player/sessionRecordingDataLogicType' import { prepareRecordingSnapshots, sessionRecordingDataLogic } from '../player/sessionRecordingDataLogic' -import { dayjs } from 'lib/dayjs' -import { Scene } from 'scenes/sceneTypes' +import type { sessionRecordingDataLogicType } from '../player/sessionRecordingDataLogicType' +import type { sessionRecordingFilePlaybackLogicType } from './sessionRecordingFilePlaybackLogicType' export type ExportedSessionRecordingFileV1 = { version: '2022-12-02' diff --git a/frontend/src/scenes/session-recordings/filters/AdvancedSessionRecordingsFilters.tsx b/frontend/src/scenes/session-recordings/filters/AdvancedSessionRecordingsFilters.tsx index e9c45d8d2efb7..45725e31c5a1e 100644 --- a/frontend/src/scenes/session-recordings/filters/AdvancedSessionRecordingsFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/AdvancedSessionRecordingsFilters.tsx @@ -1,18 +1,19 @@ -import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' - -import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { EntityTypes, FilterableLogLevel, FilterType, RecordingDurationFilter, RecordingFilters } from '~/types' -import { DateFilter } from 'lib/components/DateFilter/DateFilter' -import { DurationFilter } from './DurationFilter' import { LemonButtonWithDropdown, LemonCheckbox, LemonInput, LemonTag, Tooltip } from '@posthog/lemon-ui' -import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' import { useValues } from 'kea' -import { FEATURE_FLAGS } from 'lib/constants' +import { DateFilter } from 'lib/components/DateFilter/DateFilter' import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { FEATURE_FLAGS } from 'lib/constants' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { ActionFilter } from 'scenes/insights/filters/ActionFilter/ActionFilter' +import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' +import { TestAccountFilter } from 'scenes/insights/filters/TestAccountFilter' + import { groupsModel } from '~/models/groupsModel' +import { EntityTypes, FilterableLogLevel, FilterType, RecordingDurationFilter, RecordingFilters } from '~/types' + +import { DurationFilter } from './DurationFilter' export const AdvancedSessionRecordingsFilters = ({ filters, diff --git a/frontend/src/scenes/session-recordings/filters/DurationFilter.test.ts b/frontend/src/scenes/session-recordings/filters/DurationFilter.test.ts index 7046d99788fe3..bfc4fb0efff0a 100644 --- a/frontend/src/scenes/session-recordings/filters/DurationFilter.test.ts +++ b/frontend/src/scenes/session-recordings/filters/DurationFilter.test.ts @@ -1,4 +1,5 @@ import { DurationType, PropertyFilterType, PropertyOperator, RecordingDurationFilter } from '~/types' + import { humanFriendlyDurationFilter } from './DurationFilter' describe('DurationFilter', () => { diff --git a/frontend/src/scenes/session-recordings/filters/DurationFilter.tsx b/frontend/src/scenes/session-recordings/filters/DurationFilter.tsx index cd0ae27d24f60..8b3788e1bc801 100644 --- a/frontend/src/scenes/session-recordings/filters/DurationFilter.tsx +++ b/frontend/src/scenes/session-recordings/filters/DurationFilter.tsx @@ -1,11 +1,12 @@ -import { DurationType, PropertyOperator, RecordingDurationFilter } from '~/types' +import { LemonButton } from '@posthog/lemon-ui' +import { convertSecondsToDuration, DurationPicker } from 'lib/components/DurationPicker/DurationPicker' import { OperatorSelect } from 'lib/components/PropertyFilters/components/OperatorValueSelect' import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { DurationPicker, convertSecondsToDuration } from 'lib/components/DurationPicker/DurationPicker' -import { LemonButton } from '@posthog/lemon-ui' import { useMemo, useState } from 'react' import { DurationTypeSelect } from 'scenes/session-recordings/filters/DurationTypeSelect' +import { DurationType, PropertyOperator, RecordingDurationFilter } from '~/types' + interface Props { recordingDurationFilter: RecordingDurationFilter durationTypeFilter: DurationType diff --git a/frontend/src/scenes/session-recordings/filters/DurationTypeSelect.tsx b/frontend/src/scenes/session-recordings/filters/DurationTypeSelect.tsx index 9b72d6355b4c4..e6585bf4306d4 100644 --- a/frontend/src/scenes/session-recordings/filters/DurationTypeSelect.tsx +++ b/frontend/src/scenes/session-recordings/filters/DurationTypeSelect.tsx @@ -1,7 +1,8 @@ import { LemonSelect } from '@posthog/lemon-ui' -import { DurationType } from '~/types' import { posthog } from 'posthog-js' +import { DurationType } from '~/types' + interface DurationTypeFilterProps { // what to call this when reporting analytics to PostHog onChangeEventDescription?: string diff --git a/frontend/src/scenes/session-recordings/filters/SessionRecordingsFilters.tsx b/frontend/src/scenes/session-recordings/filters/SessionRecordingsFilters.tsx index bc1e108bf7c19..89c7ceb424313 100644 --- a/frontend/src/scenes/session-recordings/filters/SessionRecordingsFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/SessionRecordingsFilters.tsx @@ -1,10 +1,12 @@ +import { LemonButton } from '@posthog/lemon-ui' +import equal from 'fast-deep-equal' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' -import { EntityTypes, FilterType, LocalRecordingFilters, RecordingFilters } from '~/types' import { useEffect, useState } from 'react' -import equal from 'fast-deep-equal' -import { LemonButton } from '@posthog/lemon-ui' -import { SimpleSessionRecordingsFilters } from './SimpleSessionRecordingsFilters' + +import { EntityTypes, FilterType, LocalRecordingFilters, RecordingFilters } from '~/types' + import { AdvancedSessionRecordingsFilters } from './AdvancedSessionRecordingsFilters' +import { SimpleSessionRecordingsFilters } from './SimpleSessionRecordingsFilters' interface SessionRecordingsFiltersProps { filters: RecordingFilters diff --git a/frontend/src/scenes/session-recordings/filters/SimpleSessionRecordingsFilters.tsx b/frontend/src/scenes/session-recordings/filters/SimpleSessionRecordingsFilters.tsx index 372e813f6cbdd..7020e27e22404 100644 --- a/frontend/src/scenes/session-recordings/filters/SimpleSessionRecordingsFilters.tsx +++ b/frontend/src/scenes/session-recordings/filters/SimpleSessionRecordingsFilters.tsx @@ -1,3 +1,16 @@ +import { urls } from '@posthog/apps-common' +import { LemonButton, Link } from '@posthog/lemon-ui' +import { BindLogic, useActions, useValues } from 'kea' +import { TaxonomicPropertyFilter } from 'lib/components/PropertyFilters/components/TaxonomicPropertyFilter' +import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' +import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { PropertyFilterLogicProps } from 'lib/components/PropertyFilters/types' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { IconPlus } from 'lib/lemon-ui/icons' +import { Popover } from 'lib/lemon-ui/Popover/Popover' +import { useMemo, useState } from 'react' +import { teamLogic } from 'scenes/teamLogic' + import { AnyPropertyFilter, EntityTypes, @@ -6,18 +19,6 @@ import { PropertyOperator, RecordingFilters, } from '~/types' -import { useMemo, useState } from 'react' -import { BindLogic, useActions, useValues } from 'kea' -import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilterLogic' -import { TaxonomicPropertyFilter } from 'lib/components/PropertyFilters/components/TaxonomicPropertyFilter' -import { PropertyFilterLogicProps } from 'lib/components/PropertyFilters/types' -import { Popover } from 'lib/lemon-ui/Popover/Popover' -import { teamLogic } from 'scenes/teamLogic' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { LemonButton, Link } from '@posthog/lemon-ui' -import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' -import { urls } from '@posthog/apps-common' -import { IconPlus } from 'lib/lemon-ui/icons' export const SimpleSessionRecordingsFilters = ({ filters, diff --git a/frontend/src/scenes/session-recordings/player/PlayerFrame.tsx b/frontend/src/scenes/session-recordings/player/PlayerFrame.tsx index 47981346968b8..e31013c36e82b 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerFrame.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerFrame.tsx @@ -1,9 +1,10 @@ -import { useEffect, useRef } from 'react' +import './PlayerFrame.scss' + +import useSize from '@react-hook/size' import { Handler, viewportResizeDimension } from '@rrweb/types' import { useActions, useValues } from 'kea' +import { useEffect, useRef } from 'react' import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import useSize from '@react-hook/size' -import './PlayerFrame.scss' export const PlayerFrame = (): JSX.Element => { const replayDimensionRef = useRef() diff --git a/frontend/src/scenes/session-recordings/player/PlayerFrameOverlay.tsx b/frontend/src/scenes/session-recordings/player/PlayerFrameOverlay.tsx index a1bec5bc8c8e2..84af7b84828eb 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerFrameOverlay.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerFrameOverlay.tsx @@ -1,13 +1,16 @@ +import './PlayerFrameOverlay.scss' + +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { SessionPlayerState } from '~/types' import { IconErrorOutline, IconPlay } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' -import './PlayerFrameOverlay.scss' -import { PlayerUpNext } from './PlayerUpNext' import { useState } from 'react' -import clsx from 'clsx' +import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' + import { getCurrentExporterData } from '~/exporter/exporterViewLogic' +import { SessionPlayerState } from '~/types' + +import { PlayerUpNext } from './PlayerUpNext' const PlayerFrameOverlayContent = ({ currentPlayerState, diff --git a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx index ecc5f95f3ead0..354dd801ba128 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerMeta.tsx @@ -1,26 +1,29 @@ import './PlayerMeta.scss' -import { dayjs } from 'lib/dayjs' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' + +import { Link } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useValues } from 'kea' -import { PersonDisplay } from 'scenes/persons/PersonDisplay' -import { playerMetaLogic } from 'scenes/session-recordings/player/playerMetaLogic' -import { TZLabel } from 'lib/components/TZLabel' -import { percentage } from 'lib/utils' -import { IconWindow } from 'scenes/session-recordings/player/icons' import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import clsx from 'clsx' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { Link } from '@posthog/lemon-ui' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { PropertyIcon } from 'lib/components/PropertyIcon' +import { TZLabel } from 'lib/components/TZLabel' +import { dayjs } from 'lib/dayjs' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { PlayerMetaLinks } from './PlayerMetaLinks' -import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from './sessionRecordingPlayerLogic' -import { getCurrentExporterData } from '~/exporter/exporterViewLogic' -import { Logo } from '~/toolbar/assets/Logo' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { percentage } from 'lib/utils' +import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' import { asDisplay } from 'scenes/persons/person-utils' +import { PersonDisplay } from 'scenes/persons/PersonDisplay' +import { IconWindow } from 'scenes/session-recordings/player/icons' +import { playerMetaLogic } from 'scenes/session-recordings/player/playerMetaLogic' import { urls } from 'scenes/urls' -import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' + +import { getCurrentExporterData } from '~/exporter/exporterViewLogic' +import { Logo } from '~/toolbar/assets/Logo' + +import { PlayerMetaLinks } from './PlayerMetaLinks' +import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from './sessionRecordingPlayerLogic' function SessionPropertyMeta(props: { fullScreen: boolean diff --git a/frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx b/frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx index c0059f905f168..acab1e5ce8426 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerMetaLinks.tsx @@ -1,19 +1,21 @@ +import { useActions, useValues } from 'kea' +import { IconComment, IconDelete, IconLink, IconPinFilled, IconPinOutline } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { IconNotebook } from 'scenes/notebooks/IconNotebook' +import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' +import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode, } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { useActions, useValues } from 'kea' -import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' -import { IconComment, IconDelete, IconLink, IconPinFilled, IconPinOutline } from 'lib/lemon-ui/icons' import { openPlayerShareDialog } from 'scenes/session-recordings/player/share/PlayerShare' -import { PlaylistPopoverButton } from './playlist-popover/PlaylistPopover' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { NotebookSelectButton } from 'scenes/notebooks/NotebookSelectButton/NotebookSelectButton' +import { personsModalLogic } from 'scenes/trends/persons-modal/personsModalLogic' + import { NotebookNodeType } from '~/types' -import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' + import { sessionPlayerModalLogic } from './modal/sessionPlayerModalLogic' -import { personsModalLogic } from 'scenes/trends/persons-modal/personsModalLogic' -import { IconNotebook } from 'scenes/notebooks/IconNotebook' +import { PlaylistPopoverButton } from './playlist-popover/PlaylistPopover' export function PlayerMetaLinks(): JSX.Element { const { sessionRecordingId, logicProps } = useValues(sessionRecordingPlayerLogic) diff --git a/frontend/src/scenes/session-recordings/player/PlayerUpNext.tsx b/frontend/src/scenes/session-recordings/player/PlayerUpNext.tsx index 54a4c5df45c6f..db768a101cba1 100644 --- a/frontend/src/scenes/session-recordings/player/PlayerUpNext.tsx +++ b/frontend/src/scenes/session-recordings/player/PlayerUpNext.tsx @@ -1,12 +1,14 @@ import './PlayerUpNext.scss' -import { sessionRecordingPlayerLogic } from './sessionRecordingPlayerLogic' -import { CSSTransition } from 'react-transition-group' + +import clsx from 'clsx' import { BuiltLogic, useActions, useValues } from 'kea' import { IconPlay } from 'lib/lemon-ui/icons' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { useEffect, useRef, useState } from 'react' -import clsx from 'clsx' +import { CSSTransition } from 'react-transition-group' + import { sessionRecordingsPlaylistLogicType } from '../playlist/sessionRecordingsPlaylistLogicType' +import { sessionRecordingPlayerLogic } from './sessionRecordingPlayerLogic' export interface PlayerUpNextProps { playlistLogic: BuiltLogic diff --git a/frontend/src/scenes/session-recordings/player/RecordingNotFound.tsx b/frontend/src/scenes/session-recordings/player/RecordingNotFound.tsx index d3b067a32eac4..059d7c2ca89eb 100644 --- a/frontend/src/scenes/session-recordings/player/RecordingNotFound.tsx +++ b/frontend/src/scenes/session-recordings/player/RecordingNotFound.tsx @@ -1,6 +1,6 @@ +import { NotFound } from 'lib/components/NotFound' import { Link } from 'lib/lemon-ui/Link' import { urls } from 'scenes/urls' -import { NotFound } from 'lib/components/NotFound' export function RecordingNotFound(): JSX.Element { return ( diff --git a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx index 483ba76debca2..092213dd4ea4d 100644 --- a/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx +++ b/frontend/src/scenes/session-recordings/player/SessionRecordingPlayer.tsx @@ -1,6 +1,21 @@ import './SessionRecordingPlayer.scss' -import { useMemo, useRef, useState } from 'react' + +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' +import { HotkeysInterface, useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' +import { usePageVisibility } from 'lib/hooks/usePageVisibility' +import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { useMemo, useRef, useState } from 'react' +import { PlayerController } from 'scenes/session-recordings/player/controller/PlayerController' +import { PlayerInspector } from 'scenes/session-recordings/player/inspector/PlayerInspector' +import { PlayerFrame } from 'scenes/session-recordings/player/PlayerFrame' +import { RecordingNotFound } from 'scenes/session-recordings/player/RecordingNotFound' +import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' + +import { PlayerFrameOverlay } from './PlayerFrameOverlay' +import { PlayerMeta } from './PlayerMeta' +import { sessionRecordingDataLogic } from './sessionRecordingDataLogic' import { ONE_FRAME_MS, PLAYBACK_SPEEDS, @@ -8,20 +23,7 @@ import { SessionRecordingPlayerLogicProps, SessionRecordingPlayerMode, } from './sessionRecordingPlayerLogic' -import { PlayerFrame } from 'scenes/session-recordings/player/PlayerFrame' -import { PlayerController } from 'scenes/session-recordings/player/controller/PlayerController' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { PlayerInspector } from 'scenes/session-recordings/player/inspector/PlayerInspector' -import { PlayerMeta } from './PlayerMeta' -import { sessionRecordingDataLogic } from './sessionRecordingDataLogic' -import clsx from 'clsx' -import { HotkeysInterface, useKeyboardHotkeys } from 'lib/hooks/useKeyboardHotkeys' -import { usePageVisibility } from 'lib/hooks/usePageVisibility' -import { RecordingNotFound } from 'scenes/session-recordings/player/RecordingNotFound' -import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { PlayerFrameOverlay } from './PlayerFrameOverlay' import { SessionRecordingPlayerExplorer } from './view-explorer/SessionRecordingPlayerExplorer' -import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' export interface SessionRecordingPlayerProps extends SessionRecordingPlayerLogicProps { noMeta?: boolean diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx index 20b75e2795fc9..465fe05a39377 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerController.tsx @@ -1,19 +1,21 @@ +import clsx from 'clsx' import { useActions, useValues } from 'kea' +import { IconExport, IconFullScreen, IconMagnifier, IconPause, IconPlay, IconSkipInactivity } from 'lib/lemon-ui/icons' +import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { PLAYBACK_SPEEDS, - SessionRecordingPlayerMode, sessionRecordingPlayerLogic, + SessionRecordingPlayerMode, } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' + +import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' import { SessionPlayerState } from '~/types' -import { Seekbar } from './Seekbar' -import { SeekSkip } from './PlayerControllerTime' -import { LemonButton, LemonButtonWithDropdown } from 'lib/lemon-ui/LemonButton' -import { IconExport, IconFullScreen, IconMagnifier, IconPause, IconPlay, IconSkipInactivity } from 'lib/lemon-ui/icons' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import clsx from 'clsx' + import { playerSettingsLogic } from '../playerSettingsLogic' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut' +import { SeekSkip } from './PlayerControllerTime' +import { Seekbar } from './Seekbar' export function PlayerController(): JSX.Element { const { playingState, logicProps, isFullScreen } = useValues(sessionRecordingPlayerLogic) diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerControllerTime.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerControllerTime.tsx index 0ff0f9e3db16b..4cb78f98cd348 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerControllerTime.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerControllerTime.tsx @@ -1,13 +1,14 @@ -import { capitalizeFirstLetter, colonDelimitedDuration } from 'lib/utils' -import { useActions, useValues } from 'kea' -import { ONE_FRAME_MS, sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { seekbarLogic } from './seekbarLogic' +import { TZLabel } from '@posthog/apps-common' import { LemonButton, Tooltip } from '@posthog/lemon-ui' -import { useKeyHeld } from 'lib/hooks/useKeyHeld' -import { IconSkipBackward } from 'lib/lemon-ui/icons' import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { dayjs } from 'lib/dayjs' -import { TZLabel } from '@posthog/apps-common' +import { useKeyHeld } from 'lib/hooks/useKeyHeld' +import { IconSkipBackward } from 'lib/lemon-ui/icons' +import { capitalizeFirstLetter, colonDelimitedDuration } from 'lib/utils' +import { ONE_FRAME_MS, sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' + +import { seekbarLogic } from './seekbarLogic' export function Timestamp(): JSX.Element { const { logicProps, currentPlayerTime, currentTimestamp, sessionPlayerData } = diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarPreview.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarPreview.tsx index 44fd8f6763c69..01b857d66bbef 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarPreview.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarPreview.tsx @@ -1,14 +1,15 @@ +import { BindLogic, useActions, useValues } from 'kea' +import useIsHovering from 'lib/hooks/useIsHovering' import { colonDelimitedDuration } from 'lib/utils' import { MutableRefObject, useEffect, useRef, useState } from 'react' +import { useDebouncedCallback } from 'use-debounce' + import { PlayerFrame } from '../PlayerFrame' -import { BindLogic, useActions, useValues } from 'kea' import { sessionRecordingPlayerLogic, SessionRecordingPlayerLogicProps, SessionRecordingPlayerMode, } from '../sessionRecordingPlayerLogic' -import { useDebouncedCallback } from 'use-debounce' -import useIsHovering from 'lib/hooks/useIsHovering' export type PlayerSeekbarPreviewProps = { minMs: number diff --git a/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarTicks.tsx b/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarTicks.tsx index 6791b33405a02..89820398dba35 100644 --- a/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarTicks.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/PlayerSeekbarTicks.tsx @@ -1,7 +1,8 @@ import clsx from 'clsx' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { capitalizeFirstLetter, autoCaptureEventToDescription } from 'lib/utils' +import { autoCaptureEventToDescription, capitalizeFirstLetter } from 'lib/utils' import { memo } from 'react' + import { InspectorListItemEvent } from '../inspector/playerInspectorLogic' function PlayerSeekbarTick(props: { diff --git a/frontend/src/scenes/session-recordings/player/controller/Seekbar.tsx b/frontend/src/scenes/session-recordings/player/controller/Seekbar.tsx index 56e771c9531d7..670279f1f6d0c 100644 --- a/frontend/src/scenes/session-recordings/player/controller/Seekbar.tsx +++ b/frontend/src/scenes/session-recordings/player/controller/Seekbar.tsx @@ -1,15 +1,18 @@ import './Seekbar.scss' -import { useEffect, useRef } from 'react' -import { useActions, useValues } from 'kea' + import clsx from 'clsx' -import { seekbarLogic } from './seekbarLogic' +import { useActions, useValues } from 'kea' +import { useEffect, useRef } from 'react' + import { RecordingSegment } from '~/types' + +import { playerInspectorLogic } from '../inspector/playerInspectorLogic' import { sessionRecordingDataLogic } from '../sessionRecordingDataLogic' import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' import { Timestamp } from './PlayerControllerTime' -import { playerInspectorLogic } from '../inspector/playerInspectorLogic' import { PlayerSeekbarPreview } from './PlayerSeekbarPreview' import { PlayerSeekbarTicks } from './PlayerSeekbarTicks' +import { seekbarLogic } from './seekbarLogic' export function Seekbar(): JSX.Element { const { sessionRecordingId, logicProps } = useValues(sessionRecordingPlayerLogic) diff --git a/frontend/src/scenes/session-recordings/player/controller/seekbarLogic.ts b/frontend/src/scenes/session-recordings/player/controller/seekbarLogic.ts index b736e418e4663..15f8dca161329 100644 --- a/frontend/src/scenes/session-recordings/player/controller/seekbarLogic.ts +++ b/frontend/src/scenes/session-recordings/player/controller/seekbarLogic.ts @@ -1,13 +1,13 @@ -import { MutableRefObject } from 'react' import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import type { seekbarLogicType } from './seekbarLogicType' +import { clamp } from 'lib/utils' +import { MutableRefObject } from 'react' import { - SessionRecordingPlayerLogicProps, sessionRecordingPlayerLogic, + SessionRecordingPlayerLogicProps, } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { clamp } from 'lib/utils' import { getXPos, InteractEvent, ReactInteractEvent, THUMB_OFFSET, THUMB_SIZE } from '../utils/playerUtils' +import type { seekbarLogicType } from './seekbarLogicType' export const seekbarLogic = kea([ path((key) => ['scenes', 'session-recordings', 'player', 'seekbarLogic', key]), diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx index 345f6eeb0bab4..508498af73630 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspector.tsx @@ -1,6 +1,7 @@ -import { PlayerInspectorList } from './PlayerInspectorList' -import { PlayerInspectorControls } from './PlayerInspectorControls' import { useEffect, useRef, useState } from 'react' + +import { PlayerInspectorControls } from './PlayerInspectorControls' +import { PlayerInspectorList } from './PlayerInspectorList' import { PlayerInspectorPreview } from './PlayerInspectorPreview' const MOUSE_ENTER_DELAY = 100 diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx index af21f1f660a75..5a3829d1f9678 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorControls.tsx @@ -1,22 +1,24 @@ -import { LemonButton, LemonInput, LemonSelect, LemonCheckbox, Tooltip } from '@posthog/lemon-ui' -import { useValues, useActions } from 'kea' +import { LemonButton, LemonCheckbox, LemonInput, LemonSelect, Tooltip } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { + IconGauge, IconInfo, - IconSchedule, + IconPause, IconPlayCircle, - IconGauge, + IconSchedule, IconTerminal, IconUnverifiedEvent, - IconPause, } from 'lib/lemon-ui/icons' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { capitalizeFirstLetter } from 'lib/utils' -import { SessionRecordingPlayerTab } from '~/types' import { IconWindow } from 'scenes/session-recordings/player/icons' + +import { SessionRecordingPlayerTab } from '~/types' + import { playerSettingsLogic } from '../playerSettingsLogic' -import { SessionRecordingPlayerMode, sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' -import { playerInspectorLogic } from './playerInspectorLogic' +import { sessionRecordingPlayerLogic, SessionRecordingPlayerMode } from '../sessionRecordingPlayerLogic' import { InspectorSearchInfo } from './components/InspectorSearchInfo' +import { playerInspectorLogic } from './playerInspectorLogic' const TabToIcon = { [SessionRecordingPlayerTab.ALL]: undefined, diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.tsx index dfbd7566ef440..dcebd362ee685 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorList.tsx @@ -1,21 +1,24 @@ +import './PlayerInspectorList.scss' + +import { LemonButton, Link } from '@posthog/lemon-ui' +import { range } from 'd3' import { useActions, useValues } from 'kea' +import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { useEffect, useMemo, useRef } from 'react' -import { List, ListRowRenderer } from 'react-virtualized/dist/es/List' -import { CellMeasurer, CellMeasurerCache } from 'react-virtualized/dist/es/CellMeasurer' -import { AvailableFeature, SessionRecordingPlayerTab } from '~/types' -import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' -import { playerInspectorLogic } from './playerInspectorLogic' import AutoSizer from 'react-virtualized/dist/es/AutoSizer' -import { LemonButton, Link } from '@posthog/lemon-ui' -import './PlayerInspectorList.scss' -import { range } from 'd3' +import { CellMeasurer, CellMeasurerCache } from 'react-virtualized/dist/es/CellMeasurer' +import { List, ListRowRenderer } from 'react-virtualized/dist/es/List' import { teamLogic } from 'scenes/teamLogic' -import { playerSettingsLogic } from '../playerSettingsLogic' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { userLogic } from 'scenes/userLogic' -import { PayGatePage } from 'lib/components/PayGatePage/PayGatePage' -import { PlayerInspectorListItem } from './components/PlayerInspectorListItem' + import { sidePanelSettingsLogic } from '~/layout/navigation-3000/sidepanel/panels/sidePanelSettingsLogic' +import { AvailableFeature, SessionRecordingPlayerTab } from '~/types' + +import { playerSettingsLogic } from '../playerSettingsLogic' +import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' +import { PlayerInspectorListItem } from './components/PlayerInspectorListItem' +import { playerInspectorLogic } from './playerInspectorLogic' function isLocalhost(url: string | null | undefined): boolean { return !!url && ['localhost', '127.0.0.1'].includes(new URL(url).hostname) diff --git a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx index 1ed7217a36aa7..97cff5a43bc1f 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/PlayerInspectorPreview.tsx @@ -1,9 +1,11 @@ +import clsx from 'clsx' import { useValues } from 'kea' -import { IconGauge, IconTerminal, IconUnverifiedEvent, IconMagnifier } from 'lib/lemon-ui/icons' +import { IconGauge, IconMagnifier, IconTerminal, IconUnverifiedEvent } from 'lib/lemon-ui/icons' + import { SessionRecordingPlayerTab } from '~/types' + import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' import { playerInspectorLogic } from './playerInspectorLogic' -import clsx from 'clsx' const TabToIcon = { [SessionRecordingPlayerTab.ALL]: IconMagnifier, diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/ItemConsoleLog.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/ItemConsoleLog.tsx index 7a1c3cec6bbe3..878df0624374d 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/ItemConsoleLog.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/ItemConsoleLog.tsx @@ -1,6 +1,7 @@ import { LemonButton, LemonDivider } from '@posthog/lemon-ui' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' + import { InspectorListItemConsole } from '../playerInspectorLogic' export interface ItemConsoleLogProps { diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/ItemEvent.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/ItemEvent.tsx index 046ba542d8dd6..3ab3f926a967c 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/ItemEvent.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/ItemEvent.tsx @@ -1,13 +1,14 @@ import { LemonButton, LemonDivider } from '@posthog/lemon-ui' -import { IconOpenInNew } from 'lib/lemon-ui/icons' +import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { capitalizeFirstLetter, autoCaptureEventToDescription } from 'lib/utils' -import { InspectorListItemEvent } from '../playerInspectorLogic' -import { SimpleKeyValueList } from './SimpleKeyValueList' +import { IconOpenInNew } from 'lib/lemon-ui/icons' import { Spinner } from 'lib/lemon-ui/Spinner' -import { ErrorDisplay } from 'lib/components/Errors/ErrorDisplay' +import { autoCaptureEventToDescription, capitalizeFirstLetter } from 'lib/utils' import { insightUrlForEvent } from 'scenes/insights/utils' +import { InspectorListItemEvent } from '../playerInspectorLogic' +import { SimpleKeyValueList } from './SimpleKeyValueList' + export interface ItemEventProps { item: InspectorListItemEvent expanded: boolean diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx index 96c4a9e01a143..73fac6683aa02 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/ItemPerformanceEvent.tsx @@ -1,16 +1,18 @@ import { LemonButton, LemonDivider, LemonTabs, LemonTag, LemonTagType, Link } from '@posthog/lemon-ui' import clsx from 'clsx' -import { dayjs, Dayjs } from 'lib/dayjs' -import { humanizeBytes, humanFriendlyMilliseconds, isURL } from 'lib/utils' -import { Body, PerformanceEvent } from '~/types' -import { SimpleKeyValueList } from './SimpleKeyValueList' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Fragment, useState } from 'react' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' import { FlaggedFeature } from 'lib/components/FlaggedFeature' import { FEATURE_FLAGS } from 'lib/constants' +import { Dayjs, dayjs } from 'lib/dayjs' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { humanFriendlyMilliseconds, humanizeBytes, isURL } from 'lib/utils' +import { Fragment, useState } from 'react' import { NetworkRequestTiming } from 'scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming' +import { Body, PerformanceEvent } from '~/types' + +import { SimpleKeyValueList } from './SimpleKeyValueList' + const friendlyHttpStatus = { '0': 'Request not sent', '200': 'OK', @@ -140,7 +142,7 @@ export function ItemPerformanceEvent({ expanded, setExpanded, }: ItemPerformanceEvent): JSX.Element { - const [activeTab, setActiveTab] = useState<'timings' | 'headers' | 'payload' | 'response_body'>('timings') + const [activeTab, setActiveTab] = useState<'timings' | 'headers' | 'payload' | 'response_body' | 'raw'>('timings') const bytes = humanizeBytes(item.encoded_body_size || item.decoded_body_size || 0) const startTime = item.start_time || item.fetch_start || 0 @@ -176,7 +178,11 @@ export function ItemPerformanceEvent({ return acc } - if (['response_headers', 'request_headers', 'request_body', 'response_body', 'response_status'].includes(key)) { + if ( + ['response_headers', 'request_headers', 'request_body', 'response_body', 'response_status', 'raw'].includes( + key + ) + ) { return acc } @@ -392,6 +398,17 @@ export function ItemPerformanceEvent({ ), } : false, + // raw is only available if the feature flag is enabled + // TODO before proper release we should put raw behind its own flag + { + key: 'raw', + label: 'Json', + content: ( + + {JSON.stringify(item.raw, null, 2)} + + ), + }, ]} /> @@ -470,6 +487,11 @@ function StatusRow({ item }: { item: PerformanceEvent }): JSX.Element | null { let statusRow = null let methodRow = null + let fromDiskCache = false + if (item.transfer_size === 0 && item.response_body && item.response_status && item.response_status < 400) { + fromDiskCache = true + } + if (item.response_status) { const statusDescription = `${item.response_status} ${friendlyHttpStatus[item.response_status] || ''}` @@ -483,7 +505,10 @@ function StatusRow({ item }: { item: PerformanceEvent }): JSX.Element | null { statusRow = (
    Status code
    - {statusDescription} +
    + {statusDescription} + {fromDiskCache && (from cache)} +
    ) } diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx index 9478a2ceca19a..892debba08ae9 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/PlayerInspectorListItem.tsx @@ -1,12 +1,16 @@ import { TZLabel } from '@posthog/apps-common' -import { LemonDivider, LemonButton } from '@posthog/lemon-ui' +import { LemonButton, LemonDivider } from '@posthog/lemon-ui' import clsx from 'clsx' -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' +import { IconGauge, IconTerminal, IconUnverifiedEvent } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { ceilMsToClosestSecond, colonDelimitedDuration } from 'lib/utils' import { useEffect } from 'react' import { useDebouncedCallback } from 'use-debounce' import useResizeObserver from 'use-resize-observer' + import { SessionRecordingPlayerTab } from '~/types' + import { IconWindow } from '../../icons' import { playerSettingsLogic } from '../../playerSettingsLogic' import { sessionRecordingPlayerLogic } from '../../sessionRecordingPlayerLogic' @@ -14,8 +18,6 @@ import { InspectorListItem, playerInspectorLogic } from '../playerInspectorLogic import { ItemConsoleLog } from './ItemConsoleLog' import { ItemEvent } from './ItemEvent' import { ItemPerformanceEvent } from './ItemPerformanceEvent' -import { IconGauge, IconTerminal, IconUnverifiedEvent } from 'lib/lemon-ui/icons' -import { Tooltip } from 'lib/lemon-ui/Tooltip' const typeToIconAndDescription = { [SessionRecordingPlayerTab.ALL]: { diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx index a02e9bf3dce03..d2511521d3b7e 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx +++ b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming.stories.tsx @@ -1,8 +1,9 @@ -import { mswDecorator } from '~/mocks/browser' import { Meta } from '@storybook/react' -import { PerformanceEvent } from '~/types' import { NetworkRequestTiming } from 'scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming' +import { mswDecorator } from '~/mocks/browser' +import { PerformanceEvent } from '~/types' + const meta: Meta = { title: 'Components/NetworkRequestTiming', component: NetworkRequestTiming, @@ -19,27 +20,30 @@ export function Basic(): JSX.Element { + /** * There are defined sections to performance measurement. We may have data for some or all of them * @@ -109,85 +111,114 @@ function colorForSection(section: (typeof perfSections)[number]): string { * * see https://nicj.net/resourcetiming-in-practice/ */ -function calculatePerformanceParts(perfEntry: PerformanceEvent): Record { +export function calculatePerformanceParts(perfEntry: PerformanceEvent): PerformanceMeasures { const performanceParts: Record = {} - if (perfEntry.redirect_start && perfEntry.redirect_end) { - performanceParts['redirect'] = { - start: perfEntry.redirect_start, - end: perfEntry.redirect_end, - color: colorForSection('redirect'), + if (isPresent(perfEntry.redirect_start) && isPresent(perfEntry.redirect_end)) { + if (perfEntry.redirect_end - perfEntry.redirect_start > 0) { + performanceParts['redirect'] = { + start: perfEntry.redirect_start, + end: perfEntry.redirect_end, + color: colorForSection('redirect'), + } } } - if (perfEntry.fetch_start && perfEntry.domain_lookup_start) { - performanceParts['app cache'] = { - start: perfEntry.fetch_start, - end: perfEntry.domain_lookup_start, - color: colorForSection('app cache'), + if (isPresent(perfEntry.fetch_start) && isPresent(perfEntry.domain_lookup_start)) { + if (perfEntry.domain_lookup_start - perfEntry.fetch_start > 0) { + performanceParts['app cache'] = { + start: perfEntry.fetch_start, + end: perfEntry.domain_lookup_start, + color: colorForSection('app cache'), + } } } - if (perfEntry.domain_lookup_end && perfEntry.domain_lookup_start) { - performanceParts['dns lookup'] = { - start: perfEntry.domain_lookup_start, - end: perfEntry.domain_lookup_end, - color: colorForSection('dns lookup'), + if (isPresent(perfEntry.domain_lookup_end) && isPresent(perfEntry.domain_lookup_start)) { + if (perfEntry.domain_lookup_end - perfEntry.domain_lookup_start > 0) { + performanceParts['dns lookup'] = { + start: perfEntry.domain_lookup_start, + end: perfEntry.domain_lookup_end, + color: colorForSection('dns lookup'), + } } } - if (perfEntry.connect_end && perfEntry.connect_start) { - performanceParts['connection time'] = { - start: perfEntry.connect_start, - end: perfEntry.connect_end, - color: colorForSection('connection time'), - } - - if (perfEntry.secure_connection_start) { - performanceParts['tls time'] = { - start: perfEntry.secure_connection_start, + if (isPresent(perfEntry.connect_end) && isPresent(perfEntry.connect_start)) { + if (perfEntry.connect_end - perfEntry.connect_start > 0) { + performanceParts['connection time'] = { + start: perfEntry.connect_start, end: perfEntry.connect_end, - color: colorForSection('tls time'), - reducedHeight: true, + color: colorForSection('connection time'), + } + + if (isPresent(perfEntry.secure_connection_start) && perfEntry.secure_connection_start > 0) { + performanceParts['tls time'] = { + start: perfEntry.secure_connection_start, + end: perfEntry.connect_end, + color: colorForSection('tls time'), + reducedHeight: true, + } } } } - if (perfEntry.connect_end && perfEntry.request_start && perfEntry.connect_end !== perfEntry.request_start) { - performanceParts['request queuing time'] = { - start: perfEntry.connect_end, - end: perfEntry.request_start, - color: colorForSection('request queuing time'), + if ( + isPresent(perfEntry.connect_end) && + isPresent(perfEntry.request_start) && + perfEntry.connect_end !== perfEntry.request_start + ) { + if (perfEntry.request_start - perfEntry.connect_end > 0) { + performanceParts['request queuing time'] = { + start: perfEntry.connect_end, + end: perfEntry.request_start, + color: colorForSection('request queuing time'), + } } } - if (perfEntry.response_start && perfEntry.request_start) { - performanceParts['waiting for first byte'] = { - start: perfEntry.request_start, - end: perfEntry.response_start, - color: colorForSection('waiting for first byte'), + if (isPresent(perfEntry.response_start) && isPresent(perfEntry.request_start)) { + if (perfEntry.response_start - perfEntry.request_start > 0) { + performanceParts['waiting for first byte'] = { + start: perfEntry.request_start, + end: perfEntry.response_start, + color: colorForSection('waiting for first byte'), + } } } - if (perfEntry.response_start && perfEntry.response_end) { - performanceParts['receiving response'] = { - start: perfEntry.response_start, - end: perfEntry.response_end, - color: colorForSection('receiving response'), + if (isPresent(perfEntry.response_start) && isPresent(perfEntry.response_end)) { + if (perfEntry.response_end - perfEntry.response_start > 0) { + // if loading from disk cache then response_start is 0 but fetch_start is not + let start = perfEntry.response_start + if (perfEntry.response_start === 0 && isPresent(perfEntry.fetch_start)) { + start = perfEntry.fetch_start + } + performanceParts['receiving response'] = { + start: start, + end: perfEntry.response_end, + color: colorForSection('receiving response'), + } } } - if (perfEntry.response_end && perfEntry.load_event_end) { - performanceParts['document processing'] = { - start: perfEntry.response_end, - end: perfEntry.load_event_end, - color: colorForSection('document processing'), + if (isPresent(perfEntry.response_end) && isPresent(perfEntry.load_event_end)) { + if (perfEntry.load_event_end - perfEntry.response_end > 0) { + performanceParts['document processing'] = { + start: perfEntry.response_end, + end: perfEntry.load_event_end, + color: colorForSection('document processing'), + } } } return performanceParts } +function percentage(partDuration: number, totalDuration: number, min: number): number { + return Math.min(Math.max(min, (partDuration / totalDuration) * 100), 100) +} + function percentagesWithinEventRange({ partStart, partEnd, @@ -203,20 +234,20 @@ function percentagesWithinEventRange({ const partStartRelativeToTimeline = partStart - rangeStart const partDuration = partEnd - partStart - const partPercentage = Math.max(0.1, (partDuration / totalDuration) * 100) //less than 0.1% is not visible - const partStartPercentage = (partStartRelativeToTimeline / totalDuration) * 100 + const partPercentage = percentage(partDuration, totalDuration, 0.1) + const partStartPercentage = percentage(partStartRelativeToTimeline, totalDuration, 0) return { startPercentage: `${partStartPercentage}%`, widthPercentage: `${partPercentage}%` } } -const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element => { +const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element | null => { const rangeStart = performanceEvent.start_time - const rangeEnd = performanceEvent.response_end + const rangeEnd = performanceEvent.load_event_end ? performanceEvent.load_event_end : performanceEvent.response_end if (typeof rangeStart === 'number' && typeof rangeEnd === 'number') { - const performanceParts = calculatePerformanceParts(performanceEvent) + const timings = calculatePerformanceParts(performanceEvent) return (
    {perfSections.map((section) => { - const matchedSection = performanceParts[section] + const matchedSection = timings[section] const start = matchedSection?.start const end = matchedSection?.end const partDuration = end - start @@ -263,7 +294,7 @@ const TimeLineView = ({ performanceEvent }: { performanceEvent: PerformanceEvent
    ) } - return Cannot render performance timeline for this request + return null } const TableView = ({ performanceEvent }: { performanceEvent: PerformanceEvent }): JSX.Element => { @@ -283,11 +314,15 @@ export const NetworkRequestTiming = ({ }): JSX.Element | null => { const [timelineMode, setTimelineMode] = useState(true) + // if timeline view renders null then we fall back to table view + const timelineView = timelineMode ? : null + return (
    setTimelineMode(!timelineMode)} data-attr={`switch-timing-to-${timelineMode ? 'table' : 'timeline'}-view`} @@ -296,11 +331,11 @@ export const NetworkRequestTiming = ({
    - {timelineMode ? ( - - ) : ( - - )} + {timelineMode && timelineView ? timelineView : }
    ) } + +function isPresent(x: number | undefined): x is number { + return typeof x === 'number' +} diff --git a/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts new file mode 100644 index 0000000000000..ceb129e8d8e9a --- /dev/null +++ b/frontend/src/scenes/session-recordings/player/inspector/components/Timing/calculatePerformanceParts.test.ts @@ -0,0 +1,192 @@ +import { InitiatorType } from 'posthog-js' +import { calculatePerformanceParts } from 'scenes/session-recordings/player/inspector/components/Timing/NetworkRequestTiming' +import { mapRRWebNetworkRequest } from 'scenes/session-recordings/player/inspector/performance-event-utils' + +jest.mock('lib/colors', () => { + return { + getSeriesColor: jest.fn(() => '#000000'), + } +}) + +describe('calculatePerformanceParts', () => { + it('can calculate TTFB', () => { + const perfEvent = { + connect_end: 9525.599999964237, + connect_start: 9525.599999964237, + decoded_body_size: 18260, + domain_lookup_end: 9525.599999964237, + domain_lookup_start: 9525.599999964237, + duration: 935.5, + encoded_body_size: 18260, + entry_type: 'resource', + fetch_start: 9525.599999964237, + initiator_type: 'fetch', + name: 'http://localhost:8000/api/organizations/@current/plugins/repository/', + next_hop_protocol: 'http/1.1', + redirect_end: 0, + redirect_start: 0, + render_blocking_status: 'non-blocking', + request_start: 9803.099999964237, + response_end: 10461.099999964237, + response_start: 10428.399999976158, + response_status: 200, + secure_connection_start: 0, + start_time: 9525.599999964237, + time_origin: '1699990397357', + timestamp: 1699990406882, + transfer_size: 18560, + window_id: '018bcf51-b1f0-7fe0-ac05-10543621f4f2', + worker_start: 0, + uuid: '12345', + distinct_id: '23456', + session_id: 'abcde', + pageview_id: 'fghij', + current_url: 'http://localhost:8000/insights', + } + + expect(calculatePerformanceParts(perfEvent)).toEqual({ + 'request queuing time': { + color: '#000000', + end: 9803.099999964237, + start: 9525.599999964237, + }, + + 'waiting for first byte': { + color: '#000000', + end: 10428.399999976158, + start: 9803.099999964237, + }, + 'receiving response': { + color: '#000000', + end: 10461.099999964237, + start: 10428.399999976158, + }, + }) + }) + + it('can handle gravatar timings', () => { + const gravatarReqRes = { + name: 'https://www.gravatar.com/avatar/2e7d95b60efbe947f71009a1af1ba8d0?s=96&d=404', + entryType: 'resource', + initiatorType: 'fetch' as InitiatorType, + deliveryType: '', + nextHopProtocol: '', + renderBlockingStatus: 'non-blocking', + workerStart: 0, + redirectStart: 0, + redirectEnd: 0, + domainLookupStart: 0, + domainLookupEnd: 0, + connectStart: 0, + secureConnectionStart: 0, + connectEnd: 0, + requestStart: 0, + responseStart: 0, + firstInterimResponseStart: 0, + // only fetch start and response end + // and transfer size is 0 + // loaded from disk cache + startTime: 18229, + fetchStart: 18228.5, + responseEnd: 18267.5, + endTime: 18268, + duration: 39, + transferSize: 0, + encodedBodySize: 0, + decodedBodySize: 0, + responseStatus: 200, + serverTiming: [], + timeOrigin: 1700296048424, + timestamp: 1700296066652, + method: 'GET', + status: 200, + requestHeaders: {}, + requestBody: null, + responseHeaders: { + 'cache-control': 'max-age=300', + 'content-length': '13127', + 'content-type': 'image/png', + expires: 'Sat, 18 Nov 2023 08:32:46 GMT', + 'last-modified': 'Wed, 02 Feb 2022 09:11:05 GMT', + }, + responseBody: '�PNGblah', + } + const mappedToPerfEvent = mapRRWebNetworkRequest(gravatarReqRes, 'windowId', 1700296066652) + expect(calculatePerformanceParts(mappedToPerfEvent)).toEqual({ + // 'app cache' not included - end would be before beginning + // 'connection time' has 0 length + // 'dns lookup' has 0 length + // 'redirect has 0 length + // 'tls time' has 0 length + // TTFB has 0 length + 'receiving response': { + color: '#000000', + end: 18267.5, + start: 18228.5, + }, + }) + }) + + it('can handle no TLS connection timing', () => { + const tlsFreeReqRes = { + name: 'http://localhost:8000/decide/?v=3&ip=1&_=1700319068450&ver=1.91.1', + entryType: 'resource', + startTime: 6648, + duration: 93.40000003576279, + initiatorType: 'xmlhttprequest' as InitiatorType, + deliveryType: '', + nextHopProtocol: 'http/1.1', + renderBlockingStatus: 'non-blocking', + workerStart: 0, + redirectStart: 0, + redirectEnd: 0, + fetchStart: 6647.699999988079, + domainLookupStart: 6648.800000011921, + domainLookupEnd: 6648.800000011921, + connectStart: 6648.800000011921, + secureConnectionStart: 0, + connectEnd: 6649.300000011921, + requestStart: 6649.5, + responseStart: 6740.800000011921, + firstInterimResponseStart: 0, + responseEnd: 6741.100000023842, + transferSize: 2383, + encodedBodySize: 2083, + decodedBodySize: 2083, + responseStatus: 200, + serverTiming: [], + endTime: 6741, + timeOrigin: 1700319061802, + timestamp: 1700319068449, + isInitial: true, + } + const mappedToPerfEvent = mapRRWebNetworkRequest(tlsFreeReqRes, 'windowId', 1700319068449) + expect(calculatePerformanceParts(mappedToPerfEvent)).toEqual({ + 'app cache': { + color: '#000000', + end: 6648.800000011921, + start: 6647.699999988079, + }, + 'connection time': { + color: '#000000', + end: 6649.300000011921, + start: 6648.800000011921, + }, + 'waiting for first byte': { + color: '#000000', + end: 6740.800000011921, + start: 6649.5, + }, + 'receiving response': { + color: '#000000', + end: 6741.100000023842, + start: 6740.800000011921, + }, + 'request queuing time': { + color: '#000000', + end: 6649.5, + start: 6649.300000011921, + }, + }) + }) +}) diff --git a/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts b/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts index 51d9770d0e41f..502b85434f9e2 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts +++ b/frontend/src/scenes/session-recordings/player/inspector/performance-event-utils.ts @@ -1,10 +1,10 @@ import { eventWithTime } from '@rrweb/types' -import posthog from 'posthog-js' +import { CapturedNetworkRequest } from 'posthog-js' + import { PerformanceEvent } from '~/types' const NETWORK_PLUGIN_NAME = 'posthog/network@1' const RRWEB_NETWORK_PLUGIN_NAME = 'rrweb/network@1' -const IGNORED_POSTHOG_PATHS = ['/s/', '/e/', '/i/v0/e/'] export const PerformanceEventReverseMapping: { [key: number]: keyof PerformanceEvent } = { // BASE_PERFORMANCE_EVENT_COLUMNS @@ -58,8 +58,97 @@ export const PerformanceEventReverseMapping: { [key: number]: keyof PerformanceE 40: 'timestamp', } +export const RRWebPerformanceEventReverseMapping: Record = { + // BASE_PERFORMANCE_EVENT_COLUMNS + entryType: 'entry_type', + timeOrigin: 'time_origin', + name: 'name', + + // RESOURCE_EVENT_COLUMNS + startTime: 'start_time', + redirectStart: 'redirect_start', + redirectEnd: 'redirect_end', + workerStart: 'worker_start', + fetchStart: 'fetch_start', + domainLookupStart: 'domain_lookup_start', + domainLookupEnd: 'domain_lookup_end', + connectStart: 'connect_start', + secureConnectionStart: 'secure_connection_start', + connectEnd: 'connect_end', + requestStart: 'request_start', + responseStart: 'response_start', + responseEnd: 'response_end', + decodedBodySize: 'decoded_body_size', + encodedBodySize: 'encoded_body_size', + initiatorType: 'initiator_type', + nextHopProtocol: 'next_hop_protocol', + renderBlockingStatus: 'render_blocking_status', + responseStatus: 'response_status', + transferSize: 'transfer_size', + + // LARGEST_CONTENTFUL_PAINT_EVENT_COLUMNS + largestContentfulPaintElement: 'largest_contentful_paint_element', + largestContentfulPaintRenderTime: 'largest_contentful_paint_render_time', + largestContentfulPaintLoadTime: 'largest_contentful_paint_load_time', + largestContentfulPaintSize: 'largest_contentful_paint_size', + largestContentfulPaintId: 'largest_contentful_paint_id', + largestContentfulPaintUrl: 'largest_contentful_paint_url', + + // NAVIGATION_EVENT_COLUMNS + domComplete: 'dom_complete', + domContentLoadedEvent: 'dom_content_loaded_event', + domInteractive: 'dom_interactive', + loadEventEnd: 'load_event_end', + loadEventStart: 'load_event_start', + redirectCount: 'redirect_count', + navigationType: 'navigation_type', + unloadEventEnd: 'unload_event_end', + unloadEventStart: 'unload_event_start', + + // Added after v1 + duration: 'duration', + timestamp: 'timestamp', + + //rrweb/network@1 + isInitial: 'is_initial', + requestHeaders: 'request_headers', + responseHeaders: 'response_headers', + requestBody: 'request_body', + responseBody: 'response_body', + method: 'method', +} + +export function mapRRWebNetworkRequest( + capturedRequest: CapturedNetworkRequest, + windowId: string, + timestamp: PerformanceEvent['timestamp'] +): PerformanceEvent { + const data: Partial = { + timestamp: timestamp, + window_id: windowId, + raw: capturedRequest, + } + + Object.entries(RRWebPerformanceEventReverseMapping).forEach(([key, value]) => { + if (key in capturedRequest) { + data[value] = capturedRequest[key] + } + }) + + // KLUDGE: this shouldn't be necessary but let's display correctly while we figure out why it is. + if (!data.name && 'url' in capturedRequest) { + data.name = capturedRequest.url as string | undefined + } + + return data as PerformanceEvent +} + export function matchNetworkEvents(snapshotsByWindowId: Record): PerformanceEvent[] { - const eventsMapping: Record> = {} + // we only support rrweb/network@1 events or posthog/network@1 events in any one recording + // apart from during testing, where we might have both + // if we have both, we only display posthog/network@1 events + const events: PerformanceEvent[] = [] + const rrwebEvents: PerformanceEvent[] = [] // we could do this in one pass, but it's easier to log missing events // when we have all the posthog/network@1 events first @@ -83,93 +172,27 @@ export function matchNetworkEvents(snapshotsByWindowId: Record { - const snapshots = snapshotsByWindowId[1] - snapshots.forEach((snapshot: eventWithTime) => { if ( snapshot.type === 6 && // RRWeb plugin event type snapshot.data.plugin === RRWEB_NETWORK_PLUGIN_NAME ) { const payload = snapshot.data.payload as any + if (!Array.isArray(payload.requests) || payload.requests.length === 0) { return } payload.requests.forEach((capturedRequest: any) => { - const matchedURL = eventsMapping[capturedRequest.url] - - const matchedStartTime = matchedURL ? matchedURL[capturedRequest.startTime] : null - - if (matchedStartTime && matchedStartTime.length === 1) { - matchedStartTime[0].response_status = capturedRequest.status - matchedStartTime[0].request_headers = capturedRequest.requestHeaders - matchedStartTime[0].request_body = capturedRequest.requestBody - matchedStartTime[0].response_headers = capturedRequest.responseHeaders - matchedStartTime[0].response_body = capturedRequest.responseBody - matchedStartTime[0].method = capturedRequest.method - } else if (matchedStartTime && matchedStartTime.length > 1) { - // find in eventsMapping[capturedRequest.url][capturedRequest.startTime] by matching capturedRequest.endTime and element.response_end - const matchedEndTime = matchedStartTime.find( - (x) => - typeof x.response_end === 'number' && - Math.round(x.response_end) === capturedRequest.endTime - ) - if (matchedEndTime) { - matchedEndTime.response_status = capturedRequest.status - matchedEndTime.request_headers = capturedRequest.requestHeaders - matchedEndTime.request_body = capturedRequest.requestBody - matchedEndTime.response_headers = capturedRequest.responseHeaders - matchedEndTime.response_body = capturedRequest.responseBody - matchedEndTime.method = capturedRequest.method - } else { - const capturedURL = new URL(capturedRequest.url) - const capturedPath = capturedURL.pathname - - if (!IGNORED_POSTHOG_PATHS.some((x) => capturedPath === x)) { - posthog.capture('Had matches but still could not match rrweb/network@1 event', { - rrwebNetworkEvent: payload, - possibleMatches: matchedStartTime, - totalMatchedURLs: Object.keys(eventsMapping).length, - }) - } - } - } else { - const capturedURL = new URL(capturedRequest.url) - const capturedPath = capturedURL.pathname - if (!IGNORED_POSTHOG_PATHS.some((x) => capturedPath === x)) { - posthog.capture('Could not match rrweb/network@1 event', { - rrwebNetworkEvent: payload, - possibleMatches: eventsMapping[capturedRequest.url], - totalMatchedURLs: Object.keys(eventsMapping).length, - }) - } - } + const data: PerformanceEvent = mapRRWebNetworkRequest(capturedRequest, windowId, snapshot.timestamp) + + rrwebEvents.push(data) }) } }) }) - // now flatten the eventsMapping into a single array - return Object.values(eventsMapping).reduce((acc: PerformanceEvent[], eventsByURL) => { - Object.values(eventsByURL).forEach((eventsByTime) => { - acc.push(...eventsByTime) - }) - return acc - }, []) + return events.length ? events : rrwebEvents } diff --git a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts index c74b2b8f5149f..236cc3b5b8dc5 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.test.ts @@ -1,7 +1,8 @@ -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' -import { playerInspectorLogic } from 'scenes/session-recordings/player/inspector/playerInspectorLogic' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { playerInspectorLogic } from 'scenes/session-recordings/player/inspector/playerInspectorLogic' + +import { initKeaTests } from '~/test/init' const playerLogicProps = { sessionRecordingId: '1', playerKey: 'playlist' } diff --git a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts index 8c72b8e07afaa..2f0c0533b9785 100644 --- a/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts +++ b/frontend/src/scenes/session-recordings/player/inspector/playerInspectorLogic.ts @@ -1,4 +1,16 @@ +import { eventWithTime } from '@rrweb/types' +import FuseClass from 'fuse.js' import { actions, connect, events, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import api from 'lib/api' +import { Dayjs, dayjs } from 'lib/dayjs' +import { getKeyMapping } from 'lib/taxonomy' +import { eventToDescription, objectsEqual, toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { matchNetworkEvents } from 'scenes/session-recordings/player/inspector/performance-event-utils' +import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' +import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' + import { MatchedRecordingEvent, PerformanceEvent, @@ -7,20 +19,10 @@ import { RRWebRecordingConsoleLogPayload, SessionRecordingPlayerTab, } from '~/types' -import type { playerInspectorLogicType } from './playerInspectorLogicType' -import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' -import { sessionRecordingPlayerLogic, SessionRecordingPlayerLogicProps } from '../sessionRecordingPlayerLogic' + import { sessionRecordingDataLogic } from '../sessionRecordingDataLogic' -import FuseClass from 'fuse.js' -import { Dayjs, dayjs } from 'lib/dayjs' -import { getKeyMapping } from 'lib/taxonomy' -import { eventToDescription, objectsEqual, toParams } from 'lib/utils' -import { eventWithTime } from '@rrweb/types' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { loaders } from 'kea-loaders' -import api from 'lib/api' -import { matchNetworkEvents } from 'scenes/session-recordings/player/inspector/performance-event-utils' +import { sessionRecordingPlayerLogic, SessionRecordingPlayerLogicProps } from '../sessionRecordingPlayerLogic' +import type { playerInspectorLogicType } from './playerInspectorLogicType' const CONSOLE_LOG_PLUGIN_NAME = 'rrweb/console@1' diff --git a/frontend/src/scenes/session-recordings/player/modal/SessionPlayerModal.tsx b/frontend/src/scenes/session-recordings/player/modal/SessionPlayerModal.tsx index c4300a01c0906..204f5b4674ad8 100644 --- a/frontend/src/scenes/session-recordings/player/modal/SessionPlayerModal.tsx +++ b/frontend/src/scenes/session-recordings/player/modal/SessionPlayerModal.tsx @@ -1,9 +1,10 @@ -import { SessionRecordingPlayer } from 'scenes/session-recordings/player/SessionRecordingPlayer' -import { BindLogic, useActions, useValues } from 'kea' -import { sessionPlayerModalLogic } from './sessionPlayerModalLogic' import { LemonModal } from '@posthog/lemon-ui' +import { BindLogic, useActions, useValues } from 'kea' +import { SessionRecordingPlayer } from 'scenes/session-recordings/player/SessionRecordingPlayer' + import { PlayerMeta } from '../PlayerMeta' -import { SessionRecordingPlayerLogicProps, sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' +import { sessionRecordingPlayerLogic, SessionRecordingPlayerLogicProps } from '../sessionRecordingPlayerLogic' +import { sessionPlayerModalLogic } from './sessionPlayerModalLogic' export function SessionPlayerModal(): JSX.Element | null { const { activeSessionRecording } = useValues(sessionPlayerModalLogic()) diff --git a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts index 28dfc7a9280fa..838f51c78e6f5 100644 --- a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.test.ts @@ -1,8 +1,10 @@ import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' + +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' + import { sessionPlayerModalLogic } from './sessionPlayerModalLogic' -import { useMocks } from '~/mocks/jest' describe('sessionPlayerModalLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.ts b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.ts index 8f9fa214e4de3..6b70ccbd939d3 100644 --- a/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.ts +++ b/frontend/src/scenes/session-recordings/player/modal/sessionPlayerModalLogic.ts @@ -1,7 +1,8 @@ import { actions, kea, path, reducers } from 'kea' -import { SessionRecordingId, SessionRecordingType } from '~/types' import { actionToUrl, router, urlToAction } from 'kea-router' +import { SessionRecordingId, SessionRecordingType } from '~/types' + import type { sessionPlayerModalLogicType } from './sessionPlayerModalLogicType' interface HashParams { diff --git a/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts b/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts index f6c4b38c3f2b7..f21c3f7189f6e 100644 --- a/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/playerMetaLogic.test.ts @@ -1,13 +1,15 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { playerMetaLogic } from 'scenes/session-recordings/player/playerMetaLogic' import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic' import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { playerMetaLogic } from 'scenes/session-recordings/player/playerMetaLogic' -import recordingMetaJson from '../__mocks__/recording_meta.json' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' + import recordingEventsJson from '../__mocks__/recording_events_query' +import recordingMetaJson from '../__mocks__/recording_meta.json' import { snapshotsAsJSONLines } from '../__mocks__/recording_snapshots' -import { useMocks } from '~/mocks/jest' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' const playerProps = { sessionRecordingId: '1', playerKey: 'playlist' } diff --git a/frontend/src/scenes/session-recordings/player/playerMetaLogic.ts b/frontend/src/scenes/session-recordings/player/playerMetaLogic.ts index 534b324194276..5486455605458 100644 --- a/frontend/src/scenes/session-recordings/player/playerMetaLogic.ts +++ b/frontend/src/scenes/session-recordings/player/playerMetaLogic.ts @@ -1,14 +1,16 @@ +import { eventWithTime } from '@rrweb/types' import { connect, kea, key, listeners, path, props, selectors } from 'kea' -import type { playerMetaLogicType } from './playerMetaLogicType' +import { ceilMsToClosestSecond, findLastIndex, objectsEqual } from 'lib/utils' import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic' import { - SessionRecordingPlayerLogicProps, sessionRecordingPlayerLogic, + SessionRecordingPlayerLogicProps, } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { eventWithTime } from '@rrweb/types' + import { PersonType } from '~/types' -import { ceilMsToClosestSecond, findLastIndex, objectsEqual } from 'lib/utils' + import { sessionRecordingsListPropertiesLogic } from '../playlist/sessionRecordingsListPropertiesLogic' +import type { playerMetaLogicType } from './playerMetaLogicType' export const playerMetaLogic = kea([ path((key) => ['scenes', 'session-recordings', 'player', 'playerMetaLogic', key]), diff --git a/frontend/src/scenes/session-recordings/player/playerSettingsLogic.test.ts b/frontend/src/scenes/session-recordings/player/playerSettingsLogic.test.ts index 6a372a20e6841..da212eb7a77bd 100644 --- a/frontend/src/scenes/session-recordings/player/playerSettingsLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/playerSettingsLogic.test.ts @@ -1,6 +1,8 @@ import { expectLogic } from 'kea-test-utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + import { initKeaTests } from '~/test/init' + import { playerSettingsLogic } from './playerSettingsLogic' describe('playerSettingsLogic', () => { diff --git a/frontend/src/scenes/session-recordings/player/playerSettingsLogic.ts b/frontend/src/scenes/session-recordings/player/playerSettingsLogic.ts index b8166cc4da64a..1454fa2b9a4b1 100644 --- a/frontend/src/scenes/session-recordings/player/playerSettingsLogic.ts +++ b/frontend/src/scenes/session-recordings/player/playerSettingsLogic.ts @@ -1,8 +1,9 @@ import { actions, kea, listeners, path, reducers, selectors } from 'kea' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + import { AutoplayDirection, DurationType, SessionRecordingPlayerTab } from '~/types' import type { playerSettingsLogicType } from './playerSettingsLogicType' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' export type SharedListMiniFilter = { tab: SessionRecordingPlayerTab diff --git a/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx b/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx index 293ce7aff4cfc..1d19209eabcfc 100644 --- a/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx +++ b/frontend/src/scenes/session-recordings/player/playlist-popover/PlaylistPopover.tsx @@ -1,14 +1,15 @@ import { LemonCheckbox, LemonDivider } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' -import { IconPlus, IconOpenInNew, IconWithCount } from 'lib/lemon-ui/icons' +import { Field } from 'lib/forms/Field' +import { IconOpenInNew, IconPlus, IconWithCount } from 'lib/lemon-ui/icons' import { LemonButton, LemonButtonProps } from 'lib/lemon-ui/LemonButton' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { Popover } from 'lib/lemon-ui/Popover' import { Spinner } from 'lib/lemon-ui/Spinner' -import { Field } from 'lib/forms/Field' import { urls } from 'scenes/urls' + import { sessionRecordingPlayerLogic } from '../sessionRecordingPlayerLogic' import { playlistPopoverLogic } from './playlistPopoverLogic' diff --git a/frontend/src/scenes/session-recordings/player/playlist-popover/playlistPopoverLogic.ts b/frontend/src/scenes/session-recordings/player/playlist-popover/playlistPopoverLogic.ts index a5c1fa85f5967..9b26654539c24 100644 --- a/frontend/src/scenes/session-recordings/player/playlist-popover/playlistPopoverLogic.ts +++ b/frontend/src/scenes/session-recordings/player/playlist-popover/playlistPopoverLogic.ts @@ -1,18 +1,19 @@ -import { kea, props, path, key, actions, reducers, selectors, listeners, connect, afterMount } from 'kea' +import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import api from 'lib/api' import { toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { - SessionRecordingPlayerLogicProps, sessionRecordingPlayerLogic, + SessionRecordingPlayerLogicProps, } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' - -import type { playlistPopoverLogicType } from './playlistPopoverLogicType' -import { SessionRecordingPlaylistType } from '~/types' -import { forms } from 'kea-forms' import { addRecordingToPlaylist, removeRecordingFromPlaylist } from 'scenes/session-recordings/player/utils/playerUtils' import { createPlaylist } from 'scenes/session-recordings/playlist/playlistUtils' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' + +import { SessionRecordingPlaylistType } from '~/types' + +import type { playlistPopoverLogicType } from './playlistPopoverLogicType' export const playlistPopoverLogic = kea([ path((key) => ['scenes', 'session-recordings', 'player', 'playlist-popover', 'playlistPopoverLogic', key]), diff --git a/frontend/src/scenes/session-recordings/player/rrweb/index.ts b/frontend/src/scenes/session-recordings/player/rrweb/index.ts index 72f65e69c9551..3db8982a24aa0 100644 --- a/frontend/src/scenes/session-recordings/player/rrweb/index.ts +++ b/frontend/src/scenes/session-recordings/player/rrweb/index.ts @@ -1,4 +1,4 @@ -import { ReplayPlugin, playerConfig } from 'rrweb/typings/types' +import { playerConfig, ReplayPlugin } from 'rrweb/typings/types' const PROXY_URL = 'https://replay.ph-proxy.com' as const diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts index 3a9c4920e7d67..14ad52c0a62c4 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.test.ts @@ -1,21 +1,22 @@ +import { expectLogic } from 'kea-test-utils' +import { api, MOCK_TEAM_ID } from 'lib/api.mock' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { + convertSnapshotsByWindowId, prepareRecordingSnapshots, sessionRecordingDataLogic, - convertSnapshotsByWindowId, } from 'scenes/session-recordings/player/sessionRecordingDataLogic' -import { api, MOCK_TEAM_ID } from 'lib/api.mock' -import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import recordingMetaJson from '../__mocks__/recording_meta.json' -import recordingEventsJson from '../__mocks__/recording_events_query' -import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' -import { useMocks } from '~/mocks/jest' import { teamLogic } from 'scenes/teamLogic' import { userLogic } from 'scenes/userLogic' -import { AvailableFeature } from '~/types' + +import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' import { useAvailableFeatures } from '~/mocks/features' +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { AvailableFeature } from '~/types' +import recordingEventsJson from '../__mocks__/recording_events_query' +import recordingMetaJson from '../__mocks__/recording_meta.json' import { snapshotsAsJSONLines, sortedRecordingSnapshots } from '../__mocks__/recording_snapshots' const sortedRecordingSnapshotsJson = sortedRecordingSnapshots() diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts index b9d1d578b425a..623a902ae428e 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingDataLogic.ts @@ -1,7 +1,15 @@ +import { EventType, eventWithTime } from '@rrweb/types' +import { captureException } from '@sentry/react' import { actions, connect, defaults, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' +import { Dayjs, dayjs } from 'lib/dayjs' import { toParams } from 'lib/utils' +import { chainToElements } from 'lib/utils/elements-chain' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import posthog from 'posthog-js' + +import { NodeKind } from '~/queries/schema' import { AnyPropertyFilter, EncodedRecordingSnapshot, @@ -20,15 +28,9 @@ import { SessionRecordingType, SessionRecordingUsageType, } from '~/types' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { EventType, eventWithTime } from '@rrweb/types' -import { Dayjs, dayjs } from 'lib/dayjs' + import type { sessionRecordingDataLogicType } from './sessionRecordingDataLogicType' -import { chainToElements } from 'lib/utils/elements-chain' -import { captureException } from '@sentry/react' import { createSegments, mapSnapshotsToWindowId } from './utils/segmenter' -import posthog from 'posthog-js' -import { NodeKind } from '~/queries/schema' const IS_TEST_MODE = process.env.NODE_ENV === 'test' const BUFFER_MS = 60000 // +- before and after start and end of a recording to query for. diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts index 8b4526d450338..bd985c6da05d5 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.test.ts @@ -1,21 +1,22 @@ -import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' -import { initKeaTests } from '~/test/init' +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic' -import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' -import { useMocks } from '~/mocks/jest' -import { snapshotsAsJSONLines } from 'scenes/session-recordings/__mocks__/recording_snapshots' -import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' -import recordingEventsJson from 'scenes/session-recordings/__mocks__/recording_events_query' -import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import api from 'lib/api' import { MOCK_TEAM_ID } from 'lib/api.mock' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import recordingEventsJson from 'scenes/session-recordings/__mocks__/recording_events_query' +import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' +import { snapshotsAsJSONLines } from 'scenes/session-recordings/__mocks__/recording_snapshots' +import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' +import { sessionRecordingDataLogic } from 'scenes/session-recordings/player/sessionRecordingDataLogic' +import { sessionRecordingPlayerLogic } from 'scenes/session-recordings/player/sessionRecordingPlayerLogic' import { sessionRecordingsPlaylistLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { router } from 'kea-router' import { urls } from 'scenes/urls' +import { resumeKeaLoadersErrors, silenceKeaLoadersErrors } from '~/initKea' +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' + describe('sessionRecordingPlayerLogic', () => { let logic: ReturnType const mockWarn = jest.fn() diff --git a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts index eedbdd9f448f7..5d865c9dda4c8 100644 --- a/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts +++ b/frontend/src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts @@ -1,8 +1,9 @@ +import { lemonToast } from '@posthog/lemon-ui' import { - BuiltLogic, actions, afterMount, beforeUnmount, + BuiltLogic, connect, kea, key, @@ -12,38 +13,39 @@ import { reducers, selectors, } from 'kea' +import { router } from 'kea-router' +import { delay } from 'kea-test-utils' import { windowValues } from 'kea-window-values' -import type { sessionRecordingPlayerLogicType } from './sessionRecordingPlayerLogicType' -import { Replayer } from 'rrweb' +import { FEATURE_FLAGS } from 'lib/constants' +import { now } from 'lib/dayjs' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { clamp, downloadFile, fromParamsGivenUrl } from 'lib/utils' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { AvailableFeature, RecordingSegment, SessionPlayerData, SessionPlayerState } from '~/types' import { getBreakpoint } from 'lib/utils/responsiveUtils' +import { wrapConsole } from 'lib/utils/wrapConsole' +import posthog from 'posthog-js' +import { RefObject } from 'react' +import { Replayer } from 'rrweb' +import { ReplayPlugin } from 'rrweb/typings/types' +import { openBillingPopupModal } from 'scenes/billing/BillingPopup' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { - SessionRecordingDataLogicProps, sessionRecordingDataLogic, + SessionRecordingDataLogicProps, } from 'scenes/session-recordings/player/sessionRecordingDataLogic' -import { deleteRecording } from './utils/playerUtils' -import { playerSettingsLogic } from './playerSettingsLogic' -import { clamp, downloadFile, fromParamsGivenUrl } from 'lib/utils' -import { lemonToast } from '@posthog/lemon-ui' -import { delay } from 'kea-test-utils' -import { userLogic } from 'scenes/userLogic' -import { openBillingPopupModal } from 'scenes/billing/BillingPopup' import { MatchingEventsMatchType } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { router } from 'kea-router' import { urls } from 'scenes/urls' -import { wrapConsole } from 'lib/utils/wrapConsole' -import { SessionRecordingPlayerExplorerProps } from './view-explorer/SessionRecordingPlayerExplorer' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature, RecordingSegment, SessionPlayerData, SessionPlayerState } from '~/types' + import { createExportedSessionRecording } from '../file-playback/sessionRecordingFilePlaybackLogic' -import { RefObject } from 'react' -import posthog from 'posthog-js' -import { COMMON_REPLAYER_CONFIG, CorsPlugin } from './rrweb' -import { now } from 'lib/dayjs' -import { ReplayPlugin } from 'rrweb/typings/types' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' import type { sessionRecordingsPlaylistLogicType } from '../playlist/sessionRecordingsPlaylistLogicType' +import { playerSettingsLogic } from './playerSettingsLogic' +import { COMMON_REPLAYER_CONFIG, CorsPlugin } from './rrweb' +import type { sessionRecordingPlayerLogicType } from './sessionRecordingPlayerLogicType' +import { deleteRecording } from './utils/playerUtils' +import { SessionRecordingPlayerExplorerProps } from './view-explorer/SessionRecordingPlayerExplorer' export const PLAYBACK_SPEEDS = [0.5, 1, 2, 3, 4, 8, 16] export const ONE_FRAME_MS = 100 // We don't really have frames but this feels granular enough diff --git a/frontend/src/scenes/session-recordings/player/share/PlayerShare.tsx b/frontend/src/scenes/session-recordings/player/share/PlayerShare.tsx index 4c2003842efff..e0a02e72fb626 100644 --- a/frontend/src/scenes/session-recordings/player/share/PlayerShare.tsx +++ b/frontend/src/scenes/session-recordings/player/share/PlayerShare.tsx @@ -1,14 +1,15 @@ import { LemonButton, LemonCheckbox, LemonDivider, LemonInput } from '@posthog/lemon-ui' +import { captureException } from '@sentry/react' import clsx from 'clsx' import { useActions, useValues } from 'kea' import { Form } from 'kea-forms' +import { SharingModalContent } from 'lib/components/Sharing/SharingModal' +import { Field } from 'lib/forms/Field' import { IconCopy } from 'lib/lemon-ui/icons' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { Field } from 'lib/forms/Field' import { copyToClipboard } from 'lib/utils/copyToClipboard' + import { playerShareLogic, PlayerShareLogicProps } from './playerShareLogic' -import { SharingModalContent } from 'lib/components/Sharing/SharingModal' -import { captureException } from '@sentry/react' export function PlayerShareRecording(props: PlayerShareLogicProps): JSX.Element { const logic = playerShareLogic(props) diff --git a/frontend/src/scenes/session-recordings/player/share/playerShareLogic.ts b/frontend/src/scenes/session-recordings/player/share/playerShareLogic.ts index 3aa3a2994e5cc..851bcaf9d16bd 100644 --- a/frontend/src/scenes/session-recordings/player/share/playerShareLogic.ts +++ b/frontend/src/scenes/session-recordings/player/share/playerShareLogic.ts @@ -1,10 +1,10 @@ import { kea, key, path, props, selectors } from 'kea' import { forms } from 'kea-forms' +import { combineUrl } from 'kea-router' import { colonDelimitedDuration, reverseColonDelimitedDuration } from 'lib/utils' import { urls } from 'scenes/urls' import type { playerShareLogicType } from './playerShareLogicType' -import { combineUrl } from 'kea-router' export type PlayerShareLogicProps = { seconds: number | null diff --git a/frontend/src/scenes/session-recordings/player/utils/playerUtils.ts b/frontend/src/scenes/session-recordings/player/utils/playerUtils.ts index d0a877ade4c92..7fe1d5ecedceb 100644 --- a/frontend/src/scenes/session-recordings/player/utils/playerUtils.ts +++ b/frontend/src/scenes/session-recordings/player/utils/playerUtils.ts @@ -1,11 +1,12 @@ -import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react' -import { SessionRecordingPlaylistType, SessionRecordingType } from '~/types' -import { ExpandableConfig } from 'lib/lemon-ui/LemonTable' +import { router } from 'kea-router' import api from 'lib/api' +import { ExpandableConfig } from 'lib/lemon-ui/LemonTable' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { router } from 'kea-router' +import { MouseEvent as ReactMouseEvent, TouchEvent as ReactTouchEvent } from 'react' import { urls } from 'scenes/urls' +import { SessionRecordingPlaylistType, SessionRecordingType } from '~/types' + export const THUMB_SIZE = 15 export const THUMB_OFFSET = THUMB_SIZE / 2 diff --git a/frontend/src/scenes/session-recordings/player/utils/segmenter.test.ts b/frontend/src/scenes/session-recordings/player/utils/segmenter.test.ts index 3f991f693a289..e08cedf8f036f 100644 --- a/frontend/src/scenes/session-recordings/player/utils/segmenter.test.ts +++ b/frontend/src/scenes/session-recordings/player/utils/segmenter.test.ts @@ -1,10 +1,12 @@ -import { sortedRecordingSnapshots } from 'scenes/session-recordings/__mocks__/recording_snapshots' -import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' -import { createSegments } from './segmenter' -import { convertSnapshotsResponse } from '../sessionRecordingDataLogic' import { dayjs } from 'lib/dayjs' +import recordingMetaJson from 'scenes/session-recordings/__mocks__/recording_meta.json' +import { sortedRecordingSnapshots } from 'scenes/session-recordings/__mocks__/recording_snapshots' + import { RecordingSnapshot } from '~/types' +import { convertSnapshotsResponse } from '../sessionRecordingDataLogic' +import { createSegments } from './segmenter' + describe('segmenter', () => { it('matches snapshots', () => { const snapshots = convertSnapshotsResponse(sortedRecordingSnapshots().snapshot_data_by_window_id) diff --git a/frontend/src/scenes/session-recordings/player/utils/segmenter.ts b/frontend/src/scenes/session-recordings/player/utils/segmenter.ts index f2e43b459f7a1..2549c35965671 100644 --- a/frontend/src/scenes/session-recordings/player/utils/segmenter.ts +++ b/frontend/src/scenes/session-recordings/player/utils/segmenter.ts @@ -1,5 +1,6 @@ -import { EventType, IncrementalSource, eventWithTime } from '@rrweb/types' +import { EventType, eventWithTime, IncrementalSource } from '@rrweb/types' import { Dayjs } from 'lib/dayjs' + import { RecordingSegment, RecordingSnapshot } from '~/types' /** diff --git a/frontend/src/scenes/session-recordings/player/view-explorer/SessionRecordingPlayerExplorer.tsx b/frontend/src/scenes/session-recordings/player/view-explorer/SessionRecordingPlayerExplorer.tsx index 4b77d35ecfa2b..df745dc31c43f 100644 --- a/frontend/src/scenes/session-recordings/player/view-explorer/SessionRecordingPlayerExplorer.tsx +++ b/frontend/src/scenes/session-recordings/player/view-explorer/SessionRecordingPlayerExplorer.tsx @@ -1,8 +1,9 @@ +import './SessionRecordingPlayerExplorer.scss' + import { LemonButton } from '@posthog/lemon-ui' import { useResizeObserver } from 'lib/hooks/useResizeObserver' -import { useState } from 'react' -import './SessionRecordingPlayerExplorer.scss' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { useState } from 'react' export type SessionRecordingPlayerExplorerProps = { html: string diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingPreview.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingPreview.tsx index a2c34cfb26a1b..e8a2327076805 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingPreview.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingPreview.tsx @@ -1,16 +1,18 @@ -import { DurationType, SessionRecordingType } from '~/types' -import { colonDelimitedDuration } from 'lib/utils' import clsx from 'clsx' +import { useValues } from 'kea' import { PropertyIcon } from 'lib/components/PropertyIcon' -import { IconAutocapture, IconKeyboard, IconPinFilled, IconSchedule } from 'lib/lemon-ui/icons' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { TZLabel } from 'lib/components/TZLabel' +import { IconAutocapture, IconKeyboard, IconPinFilled, IconSchedule } from 'lib/lemon-ui/icons' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { colonDelimitedDuration } from 'lib/utils' import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' -import { urls } from 'scenes/urls' -import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' -import { useValues } from 'kea' import { asDisplay } from 'scenes/persons/person-utils' +import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' +import { urls } from 'scenes/urls' + +import { DurationType, SessionRecordingType } from '~/types' + import { sessionRecordingsListPropertiesLogic } from './sessionRecordingsListPropertiesLogic' export interface SessionRecordingPreviewProps { diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx index 90368ee82d0cc..3072bee23e7dd 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx @@ -1,6 +1,27 @@ -import React, { useEffect, useRef } from 'react' +import './SessionRecordingsPlaylist.scss' + +import { LemonButton, Link } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { range } from 'd3' import { BindLogic, useActions, useValues } from 'kea' -import { SessionRecordingType, ReplayTabs } from '~/types' +import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage' +import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { IconFilter, IconSettings, IconWithCount } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonTableLoader } from 'lib/lemon-ui/LemonTable/LemonTableLoader' +import { Spinner } from 'lib/lemon-ui/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import React, { useEffect, useRef } from 'react' +import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' +import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' +import { urls } from 'scenes/urls' + +import { ReplayTabs, SessionRecordingType } from '~/types' + +import { SessionRecordingsFilters } from '../filters/SessionRecordingsFilters' +import { SessionRecordingPlayer } from '../player/SessionRecordingPlayer' +import { SessionRecordingPreview, SessionRecordingPreviewSkeleton } from './SessionRecordingPreview' import { DEFAULT_RECORDING_FILTERS, defaultPageviewPropertyEntityFilter, @@ -8,26 +29,8 @@ import { SessionRecordingPlaylistLogicProps, sessionRecordingsPlaylistLogic, } from './sessionRecordingsPlaylistLogic' -import './SessionRecordingsPlaylist.scss' -import { SessionRecordingPlayer } from '../player/SessionRecordingPlayer' -import { EmptyMessage } from 'lib/components/EmptyMessage/EmptyMessage' -import { LemonButton, Link } from '@posthog/lemon-ui' -import { IconFilter, IconSettings, IconWithCount } from 'lib/lemon-ui/icons' -import clsx from 'clsx' -import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' -import { Spinner } from 'lib/lemon-ui/Spinner' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { SessionRecordingsFilters } from '../filters/SessionRecordingsFilters' -import { urls } from 'scenes/urls' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { SessionRecordingsPlaylistSettings } from './SessionRecordingsPlaylistSettings' import { SessionRecordingsPlaylistTroubleshooting } from './SessionRecordingsPlaylistTroubleshooting' -import { useNotebookNode } from 'scenes/notebooks/Nodes/NotebookNodeContext' -import { LemonTableLoader } from 'lib/lemon-ui/LemonTable/LemonTableLoader' -import { DraggableToNotebook } from 'scenes/notebooks/AddToNotebook/DraggableToNotebook' -import { range } from 'd3' -import { SessionRecordingPreview, SessionRecordingPreviewSkeleton } from './SessionRecordingPreview' const SCROLL_TRIGGER_OFFSET = 100 diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistScene.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistScene.tsx index 61f917d98ddb0..6d8c5b8ac6330 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistScene.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistScene.tsx @@ -1,16 +1,18 @@ -import { useActions, useValues } from 'kea' import './SessionRecordingsPlaylist.scss' + import { LemonButton, LemonDivider } from '@posthog/lemon-ui' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { SceneExport } from 'scenes/sceneTypes' +import { useActions, useValues } from 'kea' import { EditableField } from 'lib/components/EditableField/EditableField' -import { PageHeader } from 'lib/components/PageHeader' -import { sessionRecordingsPlaylistSceneLogic } from './sessionRecordingsPlaylistSceneLogic' import { NotFound } from 'lib/components/NotFound' +import { PageHeader } from 'lib/components/PageHeader' import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { SceneExport } from 'scenes/sceneTypes' import { playerSettingsLogic } from 'scenes/session-recordings/player/playerSettingsLogic' + import { SessionRecordingsPlaylist } from './SessionRecordingsPlaylist' +import { sessionRecordingsPlaylistSceneLogic } from './sessionRecordingsPlaylistSceneLogic' export const scene: SceneExport = { component: SessionRecordingsPlaylistScene, diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistSettings.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistSettings.tsx index 8b8f8e3ddfc56..17616e82d4059 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistSettings.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistSettings.tsx @@ -1,10 +1,11 @@ -import { useActions, useValues } from 'kea' -import { DurationTypeSelect } from 'scenes/session-recordings/filters/DurationTypeSelect' -import { playerSettingsLogic } from '../player/playerSettingsLogic' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { LemonSwitch } from '@posthog/lemon-ui' import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { IconPause, IconPlay } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { DurationTypeSelect } from 'scenes/session-recordings/filters/DurationTypeSelect' + +import { playerSettingsLogic } from '../player/playerSettingsLogic' export function SessionRecordingsPlaylistSettings(): JSX.Element { const { autoplayDirection, durationTypeToShow, hideViewedRecordings } = useValues(playerSettingsLogic) diff --git a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistTroubleshooting.tsx b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistTroubleshooting.tsx index b830a7c843b98..37ef17da6ae54 100644 --- a/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistTroubleshooting.tsx +++ b/frontend/src/scenes/session-recordings/playlist/SessionRecordingsPlaylistTroubleshooting.tsx @@ -1,5 +1,6 @@ import { Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' + import { playerSettingsLogic } from '../player/playerSettingsLogic' import { sessionRecordingsPlaylistLogic } from './sessionRecordingsPlaylistLogic' diff --git a/frontend/src/scenes/session-recordings/playlist/playlistUtils.test.ts b/frontend/src/scenes/session-recordings/playlist/playlistUtils.test.ts index fc4096abff609..d7b4025c7b20a 100644 --- a/frontend/src/scenes/session-recordings/playlist/playlistUtils.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/playlistUtils.test.ts @@ -1,6 +1,7 @@ -import { CohortType, FilterLogicalOperator, PropertyFilterType, PropertyOperator } from '~/types' import { summarizePlaylistFilters } from 'scenes/session-recordings/playlist/playlistUtils' +import { CohortType, FilterLogicalOperator, PropertyFilterType, PropertyOperator } from '~/types' + describe('summarizePlaylistFilters()', () => { const cohortIdsMapped: Partial> = { 1: { diff --git a/frontend/src/scenes/session-recordings/playlist/playlistUtils.ts b/frontend/src/scenes/session-recordings/playlist/playlistUtils.ts index 68ae4384e0504..c12e3a950cdd8 100644 --- a/frontend/src/scenes/session-recordings/playlist/playlistUtils.ts +++ b/frontend/src/scenes/session-recordings/playlist/playlistUtils.ts @@ -1,18 +1,19 @@ -import { PropertyOperator, RecordingFilters, SessionRecordingPlaylistType } from '~/types' -import { cohortsModelType } from '~/models/cohortsModelType' -import { toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' -import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' -import { genericOperatorMap } from 'lib/utils' -import { deleteWithUndo } from 'lib/utils/deleteWithUndo' -import { getKeyMapping } from 'lib/taxonomy' +import { router } from 'kea-router' import api from 'lib/api' +import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { DEFAULT_RECORDING_FILTERS } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' -import { router } from 'kea-router' -import { urls } from 'scenes/urls' +import { getKeyMapping } from 'lib/taxonomy' +import { genericOperatorMap } from 'lib/utils' +import { deleteWithUndo } from 'lib/utils/deleteWithUndo' import { openBillingPopupModal } from 'scenes/billing/BillingPopup' +import { toLocalFilters } from 'scenes/insights/filters/ActionFilter/entityFilterLogic' +import { getDisplayNameFromEntityFilter } from 'scenes/insights/utils' +import { DEFAULT_RECORDING_FILTERS } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic' import { PLAYLIST_LIMIT_REACHED_MESSAGE } from 'scenes/session-recordings/sessionRecordingsLogic' -import { convertPropertyGroupToProperties } from 'lib/components/PropertyFilters/utils' +import { urls } from 'scenes/urls' + +import { cohortsModelType } from '~/models/cohortsModelType' +import { PropertyOperator, RecordingFilters, SessionRecordingPlaylistType } from '~/types' function getOperatorSymbol(operator: PropertyOperator | null): string { if (!operator) { diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts index 0ca39dc4cc933..8a37208f27945 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.test.ts @@ -1,7 +1,8 @@ -import { useMocks } from '~/mocks/jest' -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' import { sessionRecordingsListPropertiesLogic } from 'scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic' + +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' import { SessionRecordingType } from '~/types' const mockSessons: SessionRecordingType[] = [ diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.ts index 627e99beff165..578b9a5be6523 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsListPropertiesLogic.ts @@ -1,12 +1,14 @@ -import { connect, kea, path, reducers, actions, listeners } from 'kea' +import { actions, connect, kea, listeners, path, reducers } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' -import { SessionRecordingPropertiesType, SessionRecordingType } from '~/types' +import { dayjs } from 'lib/dayjs' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import type { sessionRecordingsListPropertiesLogicType } from './sessionRecordingsListPropertiesLogicType' + import { HogQLQuery, NodeKind } from '~/queries/schema' -import { dayjs } from 'lib/dayjs' import { hogql } from '~/queries/utils' +import { SessionRecordingPropertiesType, SessionRecordingType } from '~/types' + +import type { sessionRecordingsListPropertiesLogicType } from './sessionRecordingsListPropertiesLogicType' // This logic is used to fetch properties for a list of recordings // It is used in a global way as the cached values can be re-used diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts index 550a35a3a43da..36bef7ea8faf8 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.test.ts @@ -1,15 +1,17 @@ -import { - sessionRecordingsPlaylistLogic, - RECORDINGS_LIMIT, - DEFAULT_RECORDING_FILTERS, - defaultRecordingDurationFilter, -} from './sessionRecordingsPlaylistLogic' +import { router } from 'kea-router' import { expectLogic } from 'kea-test-utils' + +import { useMocks } from '~/mocks/jest' import { initKeaTests } from '~/test/init' -import { router } from 'kea-router' import { PropertyFilterType, PropertyOperator, RecordingFilters } from '~/types' -import { useMocks } from '~/mocks/jest' + import { sessionRecordingDataLogic } from '../player/sessionRecordingDataLogic' +import { + DEFAULT_RECORDING_FILTERS, + defaultRecordingDurationFilter, + RECORDINGS_LIMIT, + sessionRecordingsPlaylistLogic, +} from './sessionRecordingsPlaylistLogic' describe('sessionRecordingsPlaylistLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts index c12c2c9352c67..130cad3bf210f 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistLogic.ts @@ -1,6 +1,13 @@ +import equal from 'fast-deep-equal' import { actions, afterMount, connect, kea, key, listeners, path, props, propsChanged, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { actionToUrl, router, urlToAction } from 'kea-router' import api from 'lib/api' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { objectClean, objectsEqual } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import posthog from 'posthog-js' + import { AnyPropertyFilter, PropertyFilterType, @@ -11,15 +18,9 @@ import { SessionRecordingsResponse, SessionRecordingType, } from '~/types' -import { actionToUrl, router, urlToAction } from 'kea-router' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import equal from 'fast-deep-equal' -import { loaders } from 'kea-loaders' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { sessionRecordingsListPropertiesLogic } from './sessionRecordingsListPropertiesLogic' -import { playerSettingsLogic } from '../player/playerSettingsLogic' -import posthog from 'posthog-js' +import { playerSettingsLogic } from '../player/playerSettingsLogic' +import { sessionRecordingsListPropertiesLogic } from './sessionRecordingsListPropertiesLogic' import type { sessionRecordingsPlaylistLogicType } from './sessionRecordingsPlaylistLogicType' export type PersonUUID = string diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.test.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.test.ts index 7e9f241596e76..4eecf62e6634f 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.test.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.test.ts @@ -1,8 +1,9 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' -import { useMocks } from '~/mocks/jest' import { sessionRecordingsPlaylistSceneLogic } from 'scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic' +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' + describe('sessionRecordingsPlaylistSceneLogic', () => { let logic: ReturnType const mockPlaylist = { diff --git a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.ts b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.ts index 761c1f0f5e2ef..a8e9b07eb3144 100644 --- a/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.ts +++ b/frontend/src/scenes/session-recordings/playlist/sessionRecordingsPlaylistSceneLogic.ts @@ -1,9 +1,9 @@ -import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { Breadcrumb, RecordingFilters, SessionRecordingPlaylistType, ReplayTabs, SessionRecordingType } from '~/types' -import { urls } from 'scenes/urls' import equal from 'fast-deep-equal' +import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { beforeUnload, router } from 'kea-router' -import { cohortsModel } from '~/models/cohortsModel' +import api from 'lib/api' +import { Scene } from 'scenes/sceneTypes' import { deletePlaylist, duplicatePlaylist, @@ -11,13 +11,14 @@ import { summarizePlaylistFilters, updatePlaylist, } from 'scenes/session-recordings/playlist/playlistUtils' -import { loaders } from 'kea-loaders' +import { urls } from 'scenes/urls' + +import { cohortsModel } from '~/models/cohortsModel' +import { Breadcrumb, RecordingFilters, ReplayTabs, SessionRecordingPlaylistType, SessionRecordingType } from '~/types' -import type { sessionRecordingsPlaylistSceneLogicType } from './sessionRecordingsPlaylistSceneLogicType' -import { PINNED_RECORDINGS_LIMIT } from './sessionRecordingsPlaylistLogic' -import api from 'lib/api' import { addRecordingToPlaylist, removeRecordingFromPlaylist } from '../player/utils/playerUtils' -import { Scene } from 'scenes/sceneTypes' +import { PINNED_RECORDINGS_LIMIT } from './sessionRecordingsPlaylistLogic' +import type { sessionRecordingsPlaylistSceneLogicType } from './sessionRecordingsPlaylistSceneLogicType' export interface SessionRecordingsPlaylistLogicProps { shortId: string diff --git a/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylists.tsx b/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylists.tsx index e5a8a07ba2eec..98cc4d7f89c61 100644 --- a/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylists.tsx +++ b/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylists.tsx @@ -1,17 +1,19 @@ -import { useActions, useValues } from 'kea' -import { ReplayTabs, SessionRecordingPlaylistType } from '~/types' -import { PLAYLISTS_PER_PAGE, savedSessionRecordingPlaylistsLogic } from './savedSessionRecordingPlaylistsLogic' +import { TZLabel } from '@posthog/apps-common' import { LemonButton, LemonDivider, LemonInput, LemonSelect, LemonTable, Link } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { useActions, useValues } from 'kea' +import { DateFilter } from 'lib/components/DateFilter/DateFilter' +import { IconCalendar, IconPinFilled, IconPinOutline } from 'lib/lemon-ui/icons' +import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { urls } from 'scenes/urls' import { createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' -import { DateFilter } from 'lib/components/DateFilter/DateFilter' import { membersLogic } from 'scenes/organization/membersLogic' -import { TZLabel } from '@posthog/apps-common' import { SavedSessionRecordingPlaylistsEmptyState } from 'scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylistsEmptyState' -import clsx from 'clsx' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { IconPinOutline, IconPinFilled, IconCalendar } from 'lib/lemon-ui/icons' +import { urls } from 'scenes/urls' + +import { ReplayTabs, SessionRecordingPlaylistType } from '~/types' + +import { PLAYLISTS_PER_PAGE, savedSessionRecordingPlaylistsLogic } from './savedSessionRecordingPlaylistsLogic' export type SavedSessionRecordingPlaylistsProps = { tab: ReplayTabs.Playlists diff --git a/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylistsEmptyState.tsx b/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylistsEmptyState.tsx index c1d647f4919a1..8397d40f303b8 100644 --- a/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylistsEmptyState.tsx +++ b/frontend/src/scenes/session-recordings/saved-playlists/SavedSessionRecordingPlaylistsEmptyState.tsx @@ -1,11 +1,13 @@ -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconPlus } from 'lib/lemon-ui/icons' -import { createPlaylist } from '../playlist/playlistUtils' import { useActions, useValues } from 'kea' +import { IconPlus } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { sceneLogic } from 'scenes/sceneLogic' -import { savedSessionRecordingPlaylistsLogic } from './savedSessionRecordingPlaylistsLogic' + import { AvailableFeature, ReplayTabs } from '~/types' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' + +import { createPlaylist } from '../playlist/playlistUtils' +import { savedSessionRecordingPlaylistsLogic } from './savedSessionRecordingPlaylistsLogic' export function SavedSessionRecordingPlaylistsEmptyState(): JSX.Element { const { guardAvailableFeature } = useActions(sceneLogic) diff --git a/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.test.ts b/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.test.ts index 52ec11c4db620..9121b4b65fb9e 100644 --- a/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.test.ts +++ b/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.test.ts @@ -1,8 +1,5 @@ -import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' import { router } from 'kea-router' -import { ReplayTabs } from '~/types' -import { useMocks } from '~/mocks/jest' +import { expectLogic } from 'kea-test-utils' import { DEFAULT_PLAYLIST_FILTERS, PLAYLISTS_PER_PAGE, @@ -10,6 +7,10 @@ import { } from 'scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic' import { urls } from 'scenes/urls' +import { useMocks } from '~/mocks/jest' +import { initKeaTests } from '~/test/init' +import { ReplayTabs } from '~/types' + describe('savedSessionRecordingPlaylistsLogic', () => { let logic: ReturnType const mockPlaylistsResponse = { diff --git a/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.ts b/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.ts index 9a6c03dec4c6c..b4b4d214c62b6 100644 --- a/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.ts +++ b/frontend/src/scenes/session-recordings/saved-playlists/savedSessionRecordingPlaylistsLogic.ts @@ -1,17 +1,19 @@ +import { lemonToast } from '@posthog/lemon-ui' import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' +import { actionToUrl, router, urlToAction } from 'kea-router' import api, { PaginatedResponse } from 'lib/api' -import { objectClean, objectsEqual, toParams } from 'lib/utils' -import { SessionRecordingPlaylistType, ReplayTabs } from '~/types' import { dayjs } from 'lib/dayjs' -import type { savedSessionRecordingPlaylistsLogicType } from './savedSessionRecordingPlaylistsLogicType' import { Sorting } from 'lib/lemon-ui/LemonTable' import { PaginationManual } from 'lib/lemon-ui/PaginationControl' -import { actionToUrl, router, urlToAction } from 'kea-router' +import { objectClean, objectsEqual, toParams } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { urls } from 'scenes/urls' + +import { ReplayTabs, SessionRecordingPlaylistType } from '~/types' + import { createPlaylist, deletePlaylist } from '../playlist/playlistUtils' -import { lemonToast } from '@posthog/lemon-ui' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import type { savedSessionRecordingPlaylistsLogicType } from './savedSessionRecordingPlaylistsLogicType' export const PLAYLISTS_PER_PAGE = 30 diff --git a/frontend/src/scenes/session-recordings/sessionRecordingsLogic.ts b/frontend/src/scenes/session-recordings/sessionRecordingsLogic.ts index be26144e3d296..c321f32b89b22 100644 --- a/frontend/src/scenes/session-recordings/sessionRecordingsLogic.ts +++ b/frontend/src/scenes/session-recordings/sessionRecordingsLogic.ts @@ -1,11 +1,13 @@ import { actions, kea, path, reducers, selectors } from 'kea' -import { Breadcrumb, ReplayTabs } from '~/types' -import { urls } from 'scenes/urls' import { actionToUrl, router, urlToAction } from 'kea-router' -import type { sessionRecordingsLogicType } from './sessionRecordingsLogicType' import { SESSION_RECORDINGS_PLAYLIST_FREE_COUNT } from 'lib/constants' import { capitalizeFirstLetter } from 'lib/utils' import { Scene } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' + +import { Breadcrumb, ReplayTabs } from '~/types' + +import type { sessionRecordingsLogicType } from './sessionRecordingsLogicType' export const humanFriendlyTabName = (tab: ReplayTabs): string => { switch (tab) { diff --git a/frontend/src/scenes/settings/Settings.tsx b/frontend/src/scenes/settings/Settings.tsx index d4b99a7750532..27bc7f669d350 100644 --- a/frontend/src/scenes/settings/Settings.tsx +++ b/frontend/src/scenes/settings/Settings.tsx @@ -1,15 +1,16 @@ +import './Settings.scss' + import { LemonBanner, LemonButton, LemonDivider } from '@posthog/lemon-ui' -import { IconChevronRight, IconLink } from 'lib/lemon-ui/icons' -import { settingsLogic } from './settingsLogic' -import { useActions, useValues } from 'kea' -import { SettingLevelIds, SettingsLogicProps } from './types' import clsx from 'clsx' -import { capitalizeFirstLetter } from 'lib/utils' +import { useActions, useValues } from 'kea' +import { NotFound } from 'lib/components/NotFound' import { useResizeBreakpoints } from 'lib/hooks/useResizeObserver' +import { IconChevronRight, IconLink } from 'lib/lemon-ui/icons' +import { capitalizeFirstLetter } from 'lib/utils' import { teamLogic } from 'scenes/teamLogic' -import './Settings.scss' -import { NotFound } from 'lib/components/NotFound' +import { settingsLogic } from './settingsLogic' +import { SettingLevelIds, SettingsLogicProps } from './types' export function Settings({ hideSections = false, diff --git a/frontend/src/scenes/settings/SettingsMap.tsx b/frontend/src/scenes/settings/SettingsMap.tsx index f5015ea7f5681..e64b368496d5a 100644 --- a/frontend/src/scenes/settings/SettingsMap.tsx +++ b/frontend/src/scenes/settings/SettingsMap.tsx @@ -1,16 +1,19 @@ -import { ChangePassword } from './user/ChangePassword' -import { OptOutCapture } from './user/OptOutCapture' -import { PersonalAPIKeys } from './user/PersonalAPIKeys' -import { TwoFactorAuthentication } from './user/TwoFactorAuthentication' -import { UpdateEmailPreferences } from './user/UpdateEmailPreferences' -import { UserDetails } from './user/UserDetails' -import { OrganizationDisplayName } from './organization/OrgDisplayName' import { Invites } from './organization/Invites' import { Members } from './organization/Members' -import { VerifiedDomains } from './organization/VerifiedDomains/VerifiedDomains' -import { OrganizationEmailPreferences } from './organization/OrgEmailPreferences' import { OrganizationDangerZone } from './organization/OrganizationDangerZone' +import { OrganizationDisplayName } from './organization/OrgDisplayName' +import { OrganizationEmailPreferences } from './organization/OrgEmailPreferences' import { PermissionsGrid } from './organization/Permissions/PermissionsGrid' +import { VerifiedDomains } from './organization/VerifiedDomains/VerifiedDomains' +import { AutocaptureSettings, ExceptionAutocaptureSettings } from './project/AutocaptureSettings' +import { CorrelationConfig } from './project/CorrelationConfig' +import { DataAttributes } from './project/DataAttributes' +import { GroupAnalyticsConfig } from './project/GroupAnalyticsConfig' +import { IPCapture } from './project/IPCapture' +import { PathCleaningFiltersConfig } from './project/PathCleaningFiltersConfig' +import { PersonDisplayNameProperties } from './project/PersonDisplayNameProperties' +import { ProjectAccessControl } from './project/ProjectAccessControl' +import { ProjectDangerZone } from './project/ProjectDangerZone' import { Bookmarklet, ProjectDisplayName, @@ -19,22 +22,19 @@ import { ProjectVariables, WebSnippet, } from './project/ProjectSettings' -import { AutocaptureSettings, ExceptionAutocaptureSettings } from './project/AutocaptureSettings' -import { DataAttributes } from './project/DataAttributes' import { ReplayAuthorizedDomains, ReplayCostControl, ReplayGeneral } from './project/SessionRecordingSettings' -import { ProjectDangerZone } from './project/ProjectDangerZone' -import { ProjectAccessControl } from './project/ProjectAccessControl' -import { ProjectAccountFiltersSetting } from './project/TestAccountFiltersConfig' -import { CorrelationConfig } from './project/CorrelationConfig' -import { PersonDisplayNameProperties } from './project/PersonDisplayNameProperties' -import { IPCapture } from './project/IPCapture' -import { WebhookIntegration } from './project/WebhookIntegration' +import { SettingPersonsOnEvents } from './project/SettingPersonsOnEvents' import { SlackIntegration } from './project/SlackIntegration' -import { PathCleaningFiltersConfig } from './project/PathCleaningFiltersConfig' -import { GroupAnalyticsConfig } from './project/GroupAnalyticsConfig' import { SurveySettings } from './project/SurveySettings' -import { SettingPersonsOnEvents } from './project/SettingPersonsOnEvents' +import { ProjectAccountFiltersSetting } from './project/TestAccountFiltersConfig' +import { WebhookIntegration } from './project/WebhookIntegration' import { SettingSection } from './types' +import { ChangePassword } from './user/ChangePassword' +import { OptOutCapture } from './user/OptOutCapture' +import { PersonalAPIKeys } from './user/PersonalAPIKeys' +import { TwoFactorAuthentication } from './user/TwoFactorAuthentication' +import { UpdateEmailPreferences } from './user/UpdateEmailPreferences' +import { UserDetails } from './user/UserDetails' export const SettingsMap: SettingSection[] = [ // PROJECT diff --git a/frontend/src/scenes/settings/SettingsScene.stories.tsx b/frontend/src/scenes/settings/SettingsScene.stories.tsx index bcfb160b24bad..404b8551df447 100644 --- a/frontend/src/scenes/settings/SettingsScene.stories.tsx +++ b/frontend/src/scenes/settings/SettingsScene.stories.tsx @@ -1,11 +1,12 @@ import { Meta } from '@storybook/react' -import { mswDecorator } from '~/mocks/browser' -import preflightJson from '~/mocks/fixtures/_preflight.json' -import { App } from 'scenes/App' -import { useEffect } from 'react' import { router } from 'kea-router' +import { useEffect } from 'react' +import { App } from 'scenes/App' import { urls } from 'scenes/urls' +import { mswDecorator } from '~/mocks/browser' +import preflightJson from '~/mocks/fixtures/_preflight.json' + const meta: Meta = { title: 'Scenes-Other/Settings', parameters: { diff --git a/frontend/src/scenes/settings/SettingsScene.tsx b/frontend/src/scenes/settings/SettingsScene.tsx index 29695feeefa37..76b82f2c1fd96 100644 --- a/frontend/src/scenes/settings/SettingsScene.tsx +++ b/frontend/src/scenes/settings/SettingsScene.tsx @@ -1,9 +1,10 @@ -import { SceneExport } from 'scenes/sceneTypes' import { useValues } from 'kea' -import { settingsSceneLogic } from './settingsSceneLogic' -import { useAnchor } from 'lib/hooks/useAnchor' import { router } from 'kea-router' +import { useAnchor } from 'lib/hooks/useAnchor' +import { SceneExport } from 'scenes/sceneTypes' + import { Settings } from './Settings' +import { settingsSceneLogic } from './settingsSceneLogic' export const scene: SceneExport = { component: SettingsScene, diff --git a/frontend/src/scenes/settings/organization/InviteModal.tsx b/frontend/src/scenes/settings/organization/InviteModal.tsx index 9e9743512547f..d0c719a294d5d 100644 --- a/frontend/src/scenes/settings/organization/InviteModal.tsx +++ b/frontend/src/scenes/settings/organization/InviteModal.tsx @@ -1,17 +1,20 @@ -import { useActions, useValues } from 'kea' import './InviteModal.scss' -import { isEmail, pluralize } from 'lib/utils' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { inviteLogic } from './inviteLogic' + +import { LemonInput, LemonTextArea, Link } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' import { IconDelete, IconPlus } from 'lib/lemon-ui/icons' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { LemonTextArea, LemonInput, Link } from '@posthog/lemon-ui' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { OrganizationInviteType } from '~/types' -import { userLogic } from 'scenes/userLogic' -import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonModal } from 'lib/lemon-ui/LemonModal' +import { isEmail, pluralize } from 'lib/utils' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' + +import { OrganizationInviteType } from '~/types' + +import { inviteLogic } from './inviteLogic' /** Shuffled placeholder names */ const PLACEHOLDER_NAMES: string[] = [...Array(10).fill('Jane'), ...Array(10).fill('John'), 'Sonic'].sort( diff --git a/frontend/src/scenes/settings/organization/Invites.tsx b/frontend/src/scenes/settings/organization/Invites.tsx index 41be87d491cf6..132184c745226 100644 --- a/frontend/src/scenes/settings/organization/Invites.tsx +++ b/frontend/src/scenes/settings/organization/Invites.tsx @@ -1,15 +1,17 @@ -import { useValues, useActions } from 'kea' -import { OrganizationInviteType } from '~/types' +import { useActions, useValues } from 'kea' import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { inviteLogic } from './inviteLogic' -import { EmailUnavailableMessage } from './InviteModal' +import { IconClose } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconClose } from 'lib/lemon-ui/icons' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' + +import { OrganizationInviteType } from '~/types' + +import { inviteLogic } from './inviteLogic' +import { EmailUnavailableMessage } from './InviteModal' function InviteLinkComponent(id: string, invite: OrganizationInviteType): JSX.Element { const url = new URL(`/signup/${id}`, document.baseURI).href diff --git a/frontend/src/scenes/settings/organization/Members.tsx b/frontend/src/scenes/settings/organization/Members.tsx index ea468523c95e2..c892bbd7dc4e9 100644 --- a/frontend/src/scenes/settings/organization/Members.tsx +++ b/frontend/src/scenes/settings/organization/Members.tsx @@ -1,27 +1,28 @@ -import { useValues, useActions } from 'kea' +import { LemonInput, LemonModal, LemonSwitch } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { TZLabel } from 'lib/components/TZLabel' import { OrganizationMembershipLevel } from 'lib/constants' -import { OrganizationMemberType } from '~/types' -import { organizationLogic } from 'scenes/organizationLogic' -import { userLogic } from 'scenes/userLogic' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { Tooltip } from 'lib/lemon-ui/Tooltip' import { getReasonForAccessLevelChangeProhibition, - organizationMembershipLevelIntegers, membershipLevelToName, + organizationMembershipLevelIntegers, } from 'lib/utils/permissioning' -import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { TZLabel } from 'lib/components/TZLabel' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { More } from 'lib/lemon-ui/LemonButton/More' -import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { LemonDivider } from 'lib/lemon-ui/LemonDivider' -import { LemonInput, LemonModal, LemonSwitch } from '@posthog/lemon-ui' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { Tooltip } from 'lib/lemon-ui/Tooltip' import { useState } from 'react' import { Setup2FA } from 'scenes/authentication/Setup2FA' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { membersLogic } from 'scenes/organization/membersLogic' +import { organizationLogic } from 'scenes/organizationLogic' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { userLogic } from 'scenes/userLogic' + +import { OrganizationMemberType } from '~/types' function ActionsComponent(_: any, member: OrganizationMemberType): JSX.Element | null { const { user } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/organization/OrgDisplayName.tsx b/frontend/src/scenes/settings/organization/OrgDisplayName.tsx index 2b65751496e6c..6f1836245ad59 100644 --- a/frontend/src/scenes/settings/organization/OrgDisplayName.tsx +++ b/frontend/src/scenes/settings/organization/OrgDisplayName.tsx @@ -1,5 +1,5 @@ -import { LemonInput, LemonButton } from '@posthog/lemon-ui' -import { useValues, useActions } from 'kea' +import { LemonButton, LemonInput } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { useRestrictedArea } from 'lib/components/RestrictedArea' import { OrganizationMembershipLevel } from 'lib/constants' import { useState } from 'react' diff --git a/frontend/src/scenes/settings/organization/OrgEmailPreferences.tsx b/frontend/src/scenes/settings/organization/OrgEmailPreferences.tsx index e2e4a1524e9e8..f427f74e684e5 100644 --- a/frontend/src/scenes/settings/organization/OrgEmailPreferences.tsx +++ b/frontend/src/scenes/settings/organization/OrgEmailPreferences.tsx @@ -1,5 +1,5 @@ import { LemonSwitch } from '@posthog/lemon-ui' -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' import { useRestrictedArea } from 'lib/components/RestrictedArea' import { OrganizationMembershipLevel } from 'lib/constants' import { organizationLogic } from 'scenes/organizationLogic' diff --git a/frontend/src/scenes/settings/organization/OrganizationDangerZone.tsx b/frontend/src/scenes/settings/organization/OrganizationDangerZone.tsx index 5425d9b67c298..937a6a9e05683 100644 --- a/frontend/src/scenes/settings/organization/OrganizationDangerZone.tsx +++ b/frontend/src/scenes/settings/organization/OrganizationDangerZone.tsx @@ -1,10 +1,10 @@ +import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { organizationLogic } from 'scenes/organizationLogic' import { useRestrictedArea } from 'lib/components/RestrictedArea' -import { Dispatch, SetStateAction, useState } from 'react' -import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' -import { IconDelete } from 'lib/lemon-ui/icons' import { OrganizationMembershipLevel } from 'lib/constants' +import { IconDelete } from 'lib/lemon-ui/icons' +import { Dispatch, SetStateAction, useState } from 'react' +import { organizationLogic } from 'scenes/organizationLogic' export function DeleteOrganizationModal({ isOpen, diff --git a/frontend/src/scenes/settings/organization/Permissions/Permissions.tsx b/frontend/src/scenes/settings/organization/Permissions/Permissions.tsx index 7adcbed6a3621..6fda5defbf015 100644 --- a/frontend/src/scenes/settings/organization/Permissions/Permissions.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/Permissions.tsx @@ -1,10 +1,12 @@ import { LemonSelect } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { RestrictedComponentProps } from 'lib/components/RestrictedArea' +import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { capitalizeFirstLetter } from 'lib/utils' + import { AccessLevel } from '~/types' -import { permissionsLogic, FormattedResourceLevel, ResourcePermissionMapping } from './permissionsLogic' + +import { FormattedResourceLevel, permissionsLogic, ResourcePermissionMapping } from './permissionsLogic' export function Permissions({ isRestricted }: RestrictedComponentProps): JSX.Element { const { allPermissions } = useValues(permissionsLogic) diff --git a/frontend/src/scenes/settings/organization/Permissions/PermissionsGrid.tsx b/frontend/src/scenes/settings/organization/Permissions/PermissionsGrid.tsx index 5f97a4c778e31..e2607e420d1e3 100644 --- a/frontend/src/scenes/settings/organization/Permissions/PermissionsGrid.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/PermissionsGrid.tsx @@ -1,18 +1,20 @@ import { LemonButton, LemonCheckbox, LemonTable } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { IconInfo } from 'lib/lemon-ui/icons' -import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' import { useRestrictedArea } from 'lib/components/RestrictedArea' import { TitleWithIcon } from 'lib/components/TitleWithIcon' +import { OrganizationMembershipLevel } from 'lib/constants' +import { IconInfo } from 'lib/lemon-ui/icons' +import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { Tooltip } from 'lib/lemon-ui/Tooltip' import { organizationLogic } from 'scenes/organizationLogic' + import { AccessLevel, AvailableFeature, Resource, RoleType } from '~/types' + import { permissionsLogic } from './permissionsLogic' import { CreateRoleModal } from './Roles/CreateRoleModal' import { rolesLogic } from './Roles/rolesLogic' import { getSingularType } from './utils' -import { OrganizationMembershipLevel } from 'lib/constants' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' export function PermissionsGrid(): JSX.Element { const { resourceRolesAccess, organizationResourcePermissionsLoading } = useValues(permissionsLogic) diff --git a/frontend/src/scenes/settings/organization/Permissions/Roles/CreateRoleModal.tsx b/frontend/src/scenes/settings/organization/Permissions/Roles/CreateRoleModal.tsx index e4cf21322dd11..a0b97bb94302b 100644 --- a/frontend/src/scenes/settings/organization/Permissions/Roles/CreateRoleModal.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/Roles/CreateRoleModal.tsx @@ -1,15 +1,17 @@ import { LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' import { IconDelete } from 'lib/lemon-ui/icons' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' import { useState } from 'react' import { organizationLogic } from 'scenes/organizationLogic' + import { RoleMemberType, UserType } from '~/types' + import { rolesLogic } from './rolesLogic' export function CreateRoleModal(): JSX.Element { diff --git a/frontend/src/scenes/settings/organization/Permissions/Roles/Roles.tsx b/frontend/src/scenes/settings/organization/Permissions/Roles/Roles.tsx index 915433298a2d3..bb8c276eb6dc6 100644 --- a/frontend/src/scenes/settings/organization/Permissions/Roles/Roles.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/Roles/Roles.tsx @@ -1,14 +1,16 @@ import { LemonButton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { RestrictedComponentProps } from 'lib/components/RestrictedArea' import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { RestrictedComponentProps } from 'lib/components/RestrictedArea' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { useState } from 'react' import { urls } from 'scenes/urls' + import { AccessLevel, RoleType } from '~/types' + import { CreateRoleModal } from './CreateRoleModal' import { rolesLogic } from './rolesLogic' -import { useState } from 'react' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' export function Roles({ isRestricted }: RestrictedComponentProps): JSX.Element { const { roles, rolesLoading } = useValues(rolesLogic) diff --git a/frontend/src/scenes/settings/organization/Permissions/Roles/rolesLogic.tsx b/frontend/src/scenes/settings/organization/Permissions/Roles/rolesLogic.tsx index 715858c30f2e4..018ba2ba5e589 100644 --- a/frontend/src/scenes/settings/organization/Permissions/Roles/rolesLogic.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/Roles/rolesLogic.tsx @@ -1,10 +1,12 @@ -import { actions, kea, reducers, path, connect, selectors, afterMount, listeners } from 'kea' +import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { teamMembersLogic } from 'scenes/settings/project/teamMembersLogic' + import { AccessLevel, Resource, RoleMemberType, RoleType, UserBasicType } from '~/types' + import type { rolesLogicType } from './rolesLogicType' -import { teamMembersLogic } from 'scenes/settings/project/teamMembersLogic' export const rolesLogic = kea([ path(['scenes', 'organization', 'rolesLogic']), diff --git a/frontend/src/scenes/settings/organization/Permissions/permissionsLogic.tsx b/frontend/src/scenes/settings/organization/Permissions/permissionsLogic.tsx index 6fccb1bd9311c..654b5b05caaa5 100644 --- a/frontend/src/scenes/settings/organization/Permissions/permissionsLogic.tsx +++ b/frontend/src/scenes/settings/organization/Permissions/permissionsLogic.tsx @@ -1,14 +1,16 @@ -import { afterMount, kea, selectors, path, connect, actions, listeners } from 'kea' +import { lemonToast } from '@posthog/lemon-ui' +import { actions, afterMount, connect, kea, listeners, path, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' -import { OrganizationResourcePermissionType, Resource, AccessLevel, RoleType } from '~/types' -import type { permissionsLogicType } from './permissionsLogicType' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' -import { rolesLogic } from './Roles/rolesLogic' -import { lemonToast } from '@posthog/lemon-ui' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { AccessLevel, OrganizationResourcePermissionType, Resource, RoleType } from '~/types' + +import type { permissionsLogicType } from './permissionsLogicType' +import { rolesLogic } from './Roles/rolesLogic' + const ResourceDisplayMapping: Record = { [Resource.FEATURE_FLAGS]: 'Feature Flags', } diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/AddDomainModal.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/AddDomainModal.tsx index b1abc4d7f185a..128ac627bb1f2 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/AddDomainModal.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/AddDomainModal.tsx @@ -1,9 +1,10 @@ import { LemonInput } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { DOMAIN_REGEX } from 'lib/constants' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { DOMAIN_REGEX } from 'lib/constants' import { useState } from 'react' + import { verifiedDomainsLogic } from './verifiedDomainsLogic' export function AddDomainModal(): JSX.Element { diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/ConfigureSAMLModal.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/ConfigureSAMLModal.tsx index a950609c6fa02..654a2f83e2b2d 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/ConfigureSAMLModal.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/ConfigureSAMLModal.tsx @@ -1,15 +1,16 @@ +import { Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { verifiedDomainsLogic } from './verifiedDomainsLogic' +import { Form } from 'kea-forms' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' import { Field } from 'lib/forms/Field' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' import { LemonModal } from 'lib/lemon-ui/LemonModal' -import { Form } from 'kea-forms' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { Link } from '@posthog/lemon-ui' + +import { verifiedDomainsLogic } from './verifiedDomainsLogic' export function ConfigureSAMLModal(): JSX.Element { const { configureSAMLModalId, isSamlConfigSubmitting, samlConfig } = useValues(verifiedDomainsLogic) diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.stories.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.stories.tsx index d1ea2e91c1b11..4b668f60c47a0 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.stories.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.stories.tsx @@ -1,9 +1,11 @@ +import { Meta, StoryFn } from '@storybook/react' import { useState } from 'react' -import { StoryFn, Meta } from '@storybook/react' -import { SSOSelect } from './SSOSelect' -import { SSOProvider } from '~/types' + import { useStorybookMocks } from '~/mocks/browser' import preflightJSON from '~/mocks/fixtures/_preflight.json' +import { SSOProvider } from '~/types' + +import { SSOSelect } from './SSOSelect' const meta: Meta = { title: 'Components/SSO Select', diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.tsx index f8072bdc57d32..abdb5ab7dfe02 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/SSOSelect.tsx @@ -3,6 +3,7 @@ import { useValues } from 'kea' import { SocialLoginIcon } from 'lib/components/SocialLoginButton/SocialLoginIcon' import { SSO_PROVIDER_NAMES } from 'lib/constants' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' + import { SSOProvider } from '~/types' export interface SSOSelectInterface { diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/VerifiedDomains.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/VerifiedDomains.tsx index f62668714b831..479e57c3057f6 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/VerifiedDomains.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/VerifiedDomains.tsx @@ -1,22 +1,24 @@ +import { IconInfo } from '@posthog/icons' import { useActions, useValues } from 'kea' -import { IconCheckmark, IconDelete, IconExclamation, IconWarning, IconLock, IconOffline } from 'lib/lemon-ui/icons' +import { IconCheckmark, IconDelete, IconExclamation, IconLock, IconOffline, IconWarning } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { More } from 'lib/lemon-ui/LemonButton/More' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' +import { Link } from 'lib/lemon-ui/Link' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { urls } from 'scenes/urls' + import { OrganizationDomainType } from '~/types' -import { verifiedDomainsLogic } from './verifiedDomainsLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { More } from 'lib/lemon-ui/LemonButton/More' + import { AddDomainModal } from './AddDomainModal' +import { ConfigureSAMLModal } from './ConfigureSAMLModal' import { SSOSelect } from './SSOSelect' +import { verifiedDomainsLogic } from './verifiedDomainsLogic' import { VerifyDomainModal } from './VerifyDomainModal' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { Link } from 'lib/lemon-ui/Link' -import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' -import { ConfigureSAMLModal } from './ConfigureSAMLModal' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' -import { IconInfo } from '@posthog/icons' -import { urls } from 'scenes/urls' const iconStyle = { marginRight: 4, fontSize: '1.15em', paddingTop: 2 } diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/VerifyDomainModal.tsx b/frontend/src/scenes/settings/organization/VerifiedDomains/VerifyDomainModal.tsx index f461bb2ef737b..6eaa28fa0f252 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/VerifyDomainModal.tsx +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/VerifyDomainModal.tsx @@ -1,9 +1,10 @@ import { useActions, useValues } from 'kea' import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { PureField } from 'lib/forms/Field' import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' -import { PureField } from 'lib/forms/Field' + import { verifiedDomainsLogic } from './verifiedDomainsLogic' export function VerifyDomainModal(): JSX.Element { diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.test.ts b/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.test.ts index ecddf961652d7..6149a36e9f6d7 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.test.ts +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.test.ts @@ -1,9 +1,11 @@ -import { isSecureURL, verifiedDomainsLogic } from './verifiedDomainsLogic' -import { initKeaTests } from '~/test/init' +import { expectLogic } from 'kea-test-utils' + import { useAvailableFeatures } from '~/mocks/features' -import { AvailableFeature } from '~/types' import { useMocks } from '~/mocks/jest' -import { expectLogic } from 'kea-test-utils' +import { initKeaTests } from '~/test/init' +import { AvailableFeature } from '~/types' + +import { isSecureURL, verifiedDomainsLogic } from './verifiedDomainsLogic' describe('verifiedDomainsLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.ts b/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.ts index a6ecafd6365bd..fbc0a829bc5ba 100644 --- a/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.ts +++ b/frontend/src/scenes/settings/organization/VerifiedDomains/verifiedDomainsLogic.ts @@ -1,12 +1,14 @@ 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 { lemonToast } from 'lib/lemon-ui/lemonToast' import { SECURE_URL_REGEX } from 'lib/constants' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { organizationLogic } from 'scenes/organizationLogic' -import { OrganizationDomainType, AvailableFeature } from '~/types' + +import { AvailableFeature, OrganizationDomainType } from '~/types' + import type { verifiedDomainsLogicType } from './verifiedDomainsLogicType' -import { forms } from 'kea-forms' -import { loaders } from 'kea-loaders' export type OrganizationDomainUpdatePayload = Partial< Pick diff --git a/frontend/src/scenes/settings/organization/inviteLogic.ts b/frontend/src/scenes/settings/organization/inviteLogic.ts index 8ebc6a1a72ef3..32865643d3a21 100644 --- a/frontend/src/scenes/settings/organization/inviteLogic.ts +++ b/frontend/src/scenes/settings/organization/inviteLogic.ts @@ -1,13 +1,15 @@ +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import { OrganizationInviteType } from '~/types' +import { router, urlToAction } from 'kea-router' import api from 'lib/api' -import { organizationLogic } from 'scenes/organizationLogic' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import type { inviteLogicType } from './inviteLogicType' +import { organizationLogic } from 'scenes/organizationLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { router, urlToAction } from 'kea-router' -import { lemonToast } from 'lib/lemon-ui/lemonToast' + +import { OrganizationInviteType } from '~/types' + +import type { inviteLogicType } from './inviteLogicType' /** State of a single invite row (with input data) in bulk invite creation. */ export interface InviteRowState { diff --git a/frontend/src/scenes/settings/organization/invitesLogic.tsx b/frontend/src/scenes/settings/organization/invitesLogic.tsx index 80db898825d58..0cb80e1dacca3 100644 --- a/frontend/src/scenes/settings/organization/invitesLogic.tsx +++ b/frontend/src/scenes/settings/organization/invitesLogic.tsx @@ -1,11 +1,13 @@ +import { events, kea, listeners, path } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, listeners, events } from 'kea' import api from 'lib/api' -import { OrganizationInviteType } from '~/types' -import type { invitesLogicType } from './invitesLogicType' +import { lemonToast } from 'lib/lemon-ui/lemonToast' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { lemonToast } from 'lib/lemon-ui/lemonToast' + +import { OrganizationInviteType } from '~/types' + +import type { invitesLogicType } from './invitesLogicType' export const invitesLogic = kea([ path(['scenes', 'organization', 'Settings', 'invitesLogic']), diff --git a/frontend/src/scenes/settings/project/AddMembersModal.tsx b/frontend/src/scenes/settings/project/AddMembersModal.tsx index 65ca193f8bb00..b049fcb372fde 100644 --- a/frontend/src/scenes/settings/project/AddMembersModal.tsx +++ b/frontend/src/scenes/settings/project/AddMembersModal.tsx @@ -1,16 +1,17 @@ -import { useState } from 'react' +import { LemonButton, LemonModal, LemonSelect, LemonSelectOption } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { teamMembersLogic } from './teamMembersLogic' -import { teamLogic } from 'scenes/teamLogic' -import { membershipLevelToName, teamMembershipLevelIntegers } from 'lib/utils/permissioning' +import { Form } from 'kea-forms' import { RestrictedComponentProps } from 'lib/components/RestrictedArea' -import { LemonButton, LemonModal, LemonSelect, LemonSelectOption } from '@posthog/lemon-ui' -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { usersLemonSelectOptions } from 'lib/components/UserSelectItem' -import { Form } from 'kea-forms' +import { TeamMembershipLevel } from 'lib/constants' import { Field } from 'lib/forms/Field' import { IconPlus } from 'lib/lemon-ui/icons' -import { TeamMembershipLevel } from 'lib/constants' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' +import { membershipLevelToName, teamMembershipLevelIntegers } from 'lib/utils/permissioning' +import { useState } from 'react' +import { teamLogic } from 'scenes/teamLogic' + +import { teamMembersLogic } from './teamMembersLogic' export function AddMembersModalWithButton({ isRestricted }: RestrictedComponentProps): JSX.Element { const { addableMembers, allMembersLoading } = useValues(teamMembersLogic) diff --git a/frontend/src/scenes/settings/project/AutocaptureSettings.tsx b/frontend/src/scenes/settings/project/AutocaptureSettings.tsx index 6b5492fc05069..8e155bbb77323 100644 --- a/frontend/src/scenes/settings/project/AutocaptureSettings.tsx +++ b/frontend/src/scenes/settings/project/AutocaptureSettings.tsx @@ -1,10 +1,11 @@ -import { useValues, useActions } from 'kea' -import { userLogic } from 'scenes/userLogic' import { LemonSwitch, LemonTag, LemonTextArea, Link } from '@posthog/lemon-ui' -import { teamLogic } from 'scenes/teamLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import clsx from 'clsx' +import { useActions, useValues } from 'kea' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { teamLogic } from 'scenes/teamLogic' +import { userLogic } from 'scenes/userLogic' + import { autocaptureExceptionsLogic } from './autocaptureExceptionsLogic' export function AutocaptureSettings(): JSX.Element { diff --git a/frontend/src/scenes/settings/project/CorrelationConfig.tsx b/frontend/src/scenes/settings/project/CorrelationConfig.tsx index 83bf547ca3913..fd2af0493a6e1 100644 --- a/frontend/src/scenes/settings/project/CorrelationConfig.tsx +++ b/frontend/src/scenes/settings/project/CorrelationConfig.tsx @@ -1,11 +1,11 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' -import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' import { EventSelect } from 'lib/components/EventSelect/EventSelect' +import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' import { IconPlus, IconSelectEvents, IconSelectProperties } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' -import { LemonButton } from '@posthog/lemon-ui' +import { teamLogic } from 'scenes/teamLogic' export function CorrelationConfig(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) diff --git a/frontend/src/scenes/settings/project/GroupAnalyticsConfig.tsx b/frontend/src/scenes/settings/project/GroupAnalyticsConfig.tsx index ea893db7132b7..061c84cdb764e 100644 --- a/frontend/src/scenes/settings/project/GroupAnalyticsConfig.tsx +++ b/frontend/src/scenes/settings/project/GroupAnalyticsConfig.tsx @@ -1,9 +1,11 @@ +import { LemonButton, LemonInput, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { GroupType } from '~/types' -import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { groupsAccessLogic, GroupsAccessStatus } from 'lib/introductions/groupsAccessLogic' -import { LemonButton, LemonInput, Link } from '@posthog/lemon-ui' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonTable, LemonTableColumns } from 'lib/lemon-ui/LemonTable' + +import { GroupType } from '~/types' + import { groupAnalyticsConfigLogic } from './groupAnalyticsConfigLogic' export function GroupAnalyticsConfig(): JSX.Element | null { diff --git a/frontend/src/scenes/settings/project/IPCapture.tsx b/frontend/src/scenes/settings/project/IPCapture.tsx index 07311b4c58e7a..7b12e6b062fe3 100644 --- a/frontend/src/scenes/settings/project/IPCapture.tsx +++ b/frontend/src/scenes/settings/project/IPCapture.tsx @@ -1,6 +1,6 @@ +import { LemonSwitch } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { teamLogic } from 'scenes/teamLogic' -import { LemonSwitch } from '@posthog/lemon-ui' export function IPCapture(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) diff --git a/frontend/src/scenes/settings/project/PathCleaningFiltersConfig.tsx b/frontend/src/scenes/settings/project/PathCleaningFiltersConfig.tsx index 076ac078020a0..c18cc609e07af 100644 --- a/frontend/src/scenes/settings/project/PathCleaningFiltersConfig.tsx +++ b/frontend/src/scenes/settings/project/PathCleaningFiltersConfig.tsx @@ -1,10 +1,11 @@ +import { Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' import { PathCleanFilters } from 'lib/components/PathCleanFilters/PathCleanFilters' -import { Link } from '@posthog/lemon-ui' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' + import { AvailableFeature, InsightType } from '~/types' -import { urls } from 'scenes/urls' export function PathCleaningFiltersConfig(): JSX.Element | null { const { updateCurrentTeam } = useActions(teamLogic) diff --git a/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx b/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx index db44c40c6a962..14a070b3e66d9 100644 --- a/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx +++ b/frontend/src/scenes/settings/project/PersonDisplayNameProperties.tsx @@ -1,8 +1,8 @@ import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { PersonPropertySelect } from 'lib/components/PersonPropertySelect/PersonPropertySelect' -import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { PERSON_DEFAULT_DISPLAY_NAME_PROPERTIES } from 'lib/constants' +import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { useEffect, useState } from 'react' import { teamLogic } from 'scenes/teamLogic' diff --git a/frontend/src/scenes/settings/project/ProjectAccessControl.tsx b/frontend/src/scenes/settings/project/ProjectAccessControl.tsx index edfdb92742a04..b34d3a52fd94c 100644 --- a/frontend/src/scenes/settings/project/ProjectAccessControl.tsx +++ b/frontend/src/scenes/settings/project/ProjectAccessControl.tsx @@ -1,27 +1,29 @@ -import { useValues, useActions } from 'kea' -import { MINIMUM_IMPLICIT_ACCESS_LEVEL, teamMembersLogic } from './teamMembersLogic' // eslint-disable-next-line no-restricted-imports -import { CloseCircleOutlined, LogoutOutlined, CrownFilled } from '@ant-design/icons' -import { humanFriendlyDetailedTime } from 'lib/utils' +import { CloseCircleOutlined, CrownFilled, LogoutOutlined } from '@ant-design/icons' +import { LemonButton, LemonSelect, LemonSelectOption, LemonSwitch, LemonTable } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { RestrictedArea, RestrictionScope, useRestrictedArea } from 'lib/components/RestrictedArea' import { OrganizationMembershipLevel, TeamMembershipLevel } from 'lib/constants' -import { FusedTeamMemberType, AvailableFeature } from '~/types' -import { userLogic } from 'scenes/userLogic' +import { IconLock, IconLockOpen } from 'lib/lemon-ui/icons' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { humanFriendlyDetailedTime } from 'lib/utils' import { getReasonForAccessLevelChangeProhibition, membershipLevelToName, teamMembershipLevelIntegers, } from 'lib/utils/permissioning' -import { AddMembersModalWithButton } from './AddMembersModal' -import { RestrictedArea, RestrictionScope, useRestrictedArea } from 'lib/components/RestrictedArea' -import { LemonButton, LemonSelect, LemonSelectOption, LemonSwitch, LemonTable } from '@posthog/lemon-ui' -import { LemonTableColumns } from 'lib/lemon-ui/LemonTable' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { organizationLogic } from 'scenes/organizationLogic' import { sceneLogic } from 'scenes/sceneLogic' -import { IconLock, IconLockOpen } from 'lib/lemon-ui/icons' +import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' +import { userLogic } from 'scenes/userLogic' + +import { AvailableFeature, FusedTeamMemberType } from '~/types' + +import { AddMembersModalWithButton } from './AddMembersModal' +import { MINIMUM_IMPLICIT_ACCESS_LEVEL, teamMembersLogic } from './teamMembersLogic' function LevelComponent(member: FusedTeamMemberType): JSX.Element | null { const { user } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/project/ProjectDangerZone.tsx b/frontend/src/scenes/settings/project/ProjectDangerZone.tsx index 82dd00ba309ef..c9807560c1c23 100644 --- a/frontend/src/scenes/settings/project/ProjectDangerZone.tsx +++ b/frontend/src/scenes/settings/project/ProjectDangerZone.tsx @@ -1,11 +1,12 @@ -import { Dispatch, SetStateAction, useState } from 'react' +import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' import { RestrictionScope, useRestrictedArea } from 'lib/components/RestrictedArea' -import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' +import { OrganizationMembershipLevel } from 'lib/constants' import { IconDelete } from 'lib/lemon-ui/icons' +import { Dispatch, SetStateAction, useState } from 'react' +import { teamLogic } from 'scenes/teamLogic' + import { TeamType } from '~/types' -import { OrganizationMembershipLevel } from 'lib/constants' export function DeleteProjectModal({ isOpen, diff --git a/frontend/src/scenes/settings/project/ProjectSettings.tsx b/frontend/src/scenes/settings/project/ProjectSettings.tsx index 3d935f09e3e56..9bb788431fa31 100644 --- a/frontend/src/scenes/settings/project/ProjectSettings.tsx +++ b/frontend/src/scenes/settings/project/ProjectSettings.tsx @@ -1,17 +1,18 @@ +import { urls } from '@posthog/apps-common' +import { LemonButton, LemonInput, LemonLabel, LemonSkeleton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' -import { JSSnippet } from 'lib/components/JSSnippet' -import { JSBookmarklet } from 'lib/components/JSBookmarklet' +import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' +import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' import { CodeSnippet } from 'lib/components/CodeSnippet' +import { JSBookmarklet } from 'lib/components/JSBookmarklet' +import { JSSnippet } from 'lib/components/JSSnippet' import { IconRefresh } from 'lib/lemon-ui/icons' import { Link } from 'lib/lemon-ui/Link' -import { LemonButton, LemonInput, LemonLabel, LemonSkeleton } from '@posthog/lemon-ui' import { useState } from 'react' +import { isAuthenticatedTeam, teamLogic } from 'scenes/teamLogic' + import { TimezoneConfig } from './TimezoneConfig' import { WeekStartConfig } from './WeekStartConfig' -import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' -import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' -import { urls } from '@posthog/apps-common' export function ProjectDisplayName(): JSX.Element { const { currentTeam, currentTeamLoading } = useValues(teamLogic) diff --git a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx index e16f1167ceebd..44ff966db0c65 100644 --- a/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx +++ b/frontend/src/scenes/settings/project/SessionRecordingSettings.tsx @@ -1,14 +1,14 @@ -import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' import { LemonBanner, LemonButton, LemonSelect, LemonSwitch, Link } from '@posthog/lemon-ui' -import { urls } from 'scenes/urls' +import { useActions, useValues } from 'kea' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' -import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' import { FlaggedFeature } from 'lib/components/FlaggedFeature' +import { FlagSelector } from 'lib/components/FlagSelector' import { FEATURE_FLAGS } from 'lib/constants' import { IconCancel } from 'lib/lemon-ui/icons' -import { FlagSelector } from 'lib/components/FlagSelector' +import { LemonLabel } from 'lib/lemon-ui/LemonLabel/LemonLabel' +import { teamLogic } from 'scenes/teamLogic' +import { urls } from 'scenes/urls' export function ReplayGeneral(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) diff --git a/frontend/src/scenes/settings/project/SettingPersonsOnEvents.tsx b/frontend/src/scenes/settings/project/SettingPersonsOnEvents.tsx index 031def3241109..369f0803c1994 100644 --- a/frontend/src/scenes/settings/project/SettingPersonsOnEvents.tsx +++ b/frontend/src/scenes/settings/project/SettingPersonsOnEvents.tsx @@ -1,6 +1,6 @@ +import { LemonSwitch, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { teamLogic } from 'scenes/teamLogic' -import { LemonSwitch, Link } from '@posthog/lemon-ui' export function SettingPersonsOnEvents(): JSX.Element { const { updateCurrentTeam } = useActions(teamLogic) diff --git a/frontend/src/scenes/settings/project/SlackIntegration.stories.tsx b/frontend/src/scenes/settings/project/SlackIntegration.stories.tsx index aa21717e2cbf5..51a240d83f2a4 100644 --- a/frontend/src/scenes/settings/project/SlackIntegration.stories.tsx +++ b/frontend/src/scenes/settings/project/SlackIntegration.stories.tsx @@ -1,8 +1,10 @@ import { Meta } from '@storybook/react' -import { AvailableFeature } from '~/types' -import { useAvailableFeatures } from '~/mocks/features' + import { useStorybookMocks } from '~/mocks/browser' +import { useAvailableFeatures } from '~/mocks/features' import { mockIntegration } from '~/test/mocks' +import { AvailableFeature } from '~/types' + import { SlackIntegration } from './SlackIntegration' const meta: Meta = { diff --git a/frontend/src/scenes/settings/project/SlackIntegration.tsx b/frontend/src/scenes/settings/project/SlackIntegration.tsx index e74ef84a8fa01..1f40b80e25281 100644 --- a/frontend/src/scenes/settings/project/SlackIntegration.tsx +++ b/frontend/src/scenes/settings/project/SlackIntegration.tsx @@ -1,13 +1,14 @@ -import { useState } from 'react' +import { LemonButton, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { getSlackAppManifest, integrationsLogic } from './integrationsLogic' import { CodeSnippet, Language } from 'lib/components/CodeSnippet' -import { LemonButton, Link } from '@posthog/lemon-ui' -import { IconDelete, IconSlack } from 'lib/lemon-ui/icons' import { UserActivityIndicator } from 'lib/components/UserActivityIndicator/UserActivityIndicator' +import { IconDelete, IconSlack } from 'lib/lemon-ui/icons' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { useState } from 'react' import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' + +import { getSlackAppManifest, integrationsLogic } from './integrationsLogic' export function SlackIntegration(): JSX.Element { const { slackIntegration, addToSlackButtonUrl } = useValues(integrationsLogic) diff --git a/frontend/src/scenes/settings/project/TestAccountFiltersConfig.tsx b/frontend/src/scenes/settings/project/TestAccountFiltersConfig.tsx index 58cd23f6288e1..28ddaf24fad76 100644 --- a/frontend/src/scenes/settings/project/TestAccountFiltersConfig.tsx +++ b/frontend/src/scenes/settings/project/TestAccountFiltersConfig.tsx @@ -1,12 +1,14 @@ +import { LemonSwitch, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { teamLogic } from 'scenes/teamLogic' -import { AnyPropertyFilter } from '~/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' + import { groupsModel } from '~/models/groupsModel' -import { LemonSwitch, Link } from '@posthog/lemon-ui' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { AnyPropertyFilter } from '~/types' + import { filterTestAccountsDefaultsLogic } from './filterTestAccountDefaultsLogic' function TestAccountFiltersConfig(): JSX.Element { diff --git a/frontend/src/scenes/settings/project/TimezoneConfig.tsx b/frontend/src/scenes/settings/project/TimezoneConfig.tsx index 719b9a94e921c..f596f162115df 100644 --- a/frontend/src/scenes/settings/project/TimezoneConfig.tsx +++ b/frontend/src/scenes/settings/project/TimezoneConfig.tsx @@ -1,10 +1,9 @@ import { useActions, useValues } from 'kea' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' -import { teamLogic } from 'scenes/teamLogic' - -import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { LemonSelectMultiple } from 'lib/lemon-ui/LemonSelectMultiple/LemonSelectMultiple' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { teamLogic } from 'scenes/teamLogic' const tzLabel = (tz: string, offset: number): string => `${tz.replace(/\//g, ' / ').replace(/_/g, ' ')} (UTC${offset === 0 ? '±' : offset > 0 ? '+' : '-'}${Math.abs( diff --git a/frontend/src/scenes/settings/project/WebhookIntegration.tsx b/frontend/src/scenes/settings/project/WebhookIntegration.tsx index f2fae688f7340..d17cfba9cd639 100644 --- a/frontend/src/scenes/settings/project/WebhookIntegration.tsx +++ b/frontend/src/scenes/settings/project/WebhookIntegration.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState } from 'react' +import { LemonButton, LemonInput, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { supportLogic } from 'lib/components/Support/supportLogic' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { useEffect, useState } from 'react' import { teamLogic } from 'scenes/teamLogic' + import { webhookIntegrationLogic } from './webhookIntegrationLogic' -import { LemonButton, LemonInput, Link } from '@posthog/lemon-ui' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import { supportLogic } from 'lib/components/Support/supportLogic' export function WebhookIntegration(): JSX.Element { const [webhook, setWebhook] = useState('') diff --git a/frontend/src/scenes/settings/project/WeekStartConfig.tsx b/frontend/src/scenes/settings/project/WeekStartConfig.tsx index 4a00d35729c23..a4577b16a703d 100644 --- a/frontend/src/scenes/settings/project/WeekStartConfig.tsx +++ b/frontend/src/scenes/settings/project/WeekStartConfig.tsx @@ -1,7 +1,7 @@ -import { useActions, useValues } from 'kea' -import { teamLogic } from 'scenes/teamLogic' import { LemonSelect } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { LemonDialog } from 'lib/lemon-ui/LemonDialog' +import { teamLogic } from 'scenes/teamLogic' export function WeekStartConfig(): JSX.Element { const { currentTeam, currentTeamLoading } = useValues(teamLogic) diff --git a/frontend/src/scenes/settings/project/autocaptureExceptionsLogic.ts b/frontend/src/scenes/settings/project/autocaptureExceptionsLogic.ts index 9aa7a465bdb0c..b9be34b208c50 100644 --- a/frontend/src/scenes/settings/project/autocaptureExceptionsLogic.ts +++ b/frontend/src/scenes/settings/project/autocaptureExceptionsLogic.ts @@ -1,4 +1,4 @@ -import { kea, reducers, actions, listeners, selectors, connect, path, afterMount } from 'kea' +import { actions, afterMount, connect, kea, listeners, path, reducers, selectors } from 'kea' import { teamLogic } from 'scenes/teamLogic' import type { autocaptureExceptionsLogicType } from './autocaptureExceptionsLogicType' diff --git a/frontend/src/scenes/settings/project/filterTestAccountDefaultsLogic.ts b/frontend/src/scenes/settings/project/filterTestAccountDefaultsLogic.ts index ba3fee5cb8a17..0616394c5ef24 100644 --- a/frontend/src/scenes/settings/project/filterTestAccountDefaultsLogic.ts +++ b/frontend/src/scenes/settings/project/filterTestAccountDefaultsLogic.ts @@ -1,4 +1,4 @@ -import { kea, path, reducers, actions, listeners, events, selectors, connect } from 'kea' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' import { teamLogic } from 'scenes/teamLogic' import type { filterTestAccountsDefaultsLogicType } from './filterTestAccountDefaultsLogicType' diff --git a/frontend/src/scenes/settings/project/groupAnalyticsConfigLogic.ts b/frontend/src/scenes/settings/project/groupAnalyticsConfigLogic.ts index dd1ec134bbd88..3edfe58f5b7d1 100644 --- a/frontend/src/scenes/settings/project/groupAnalyticsConfigLogic.ts +++ b/frontend/src/scenes/settings/project/groupAnalyticsConfigLogic.ts @@ -1,5 +1,7 @@ -import { kea, path, connect, actions, reducers, selectors, listeners } from 'kea' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' + import { groupsModel } from '~/models/groupsModel' + import type { groupAnalyticsConfigLogicType } from './groupAnalyticsConfigLogicType' export const groupAnalyticsConfigLogic = kea([ diff --git a/frontend/src/scenes/settings/project/integrationsLogic.ts b/frontend/src/scenes/settings/project/integrationsLogic.ts index be287440db304..55c90ac929b79 100644 --- a/frontend/src/scenes/settings/project/integrationsLogic.ts +++ b/frontend/src/scenes/settings/project/integrationsLogic.ts @@ -1,10 +1,11 @@ import { lemonToast } from '@posthog/lemon-ui' -import { kea, path, listeners, selectors, connect, afterMount, actions } from 'kea' +import { actions, afterMount, connect, kea, listeners, path, selectors } from 'kea' import { loaders } from 'kea-loaders' import { router, urlToAction } from 'kea-router' import api from 'lib/api' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' import { urls } from 'scenes/urls' + import { IntegrationType, SlackChannelType } from '~/types' import type { integrationsLogicType } from './integrationsLogicType' diff --git a/frontend/src/scenes/settings/project/teamMembersLogic.tsx b/frontend/src/scenes/settings/project/teamMembersLogic.tsx index d6d137b48a2be..f9457bca55b00 100644 --- a/frontend/src/scenes/settings/project/teamMembersLogic.tsx +++ b/frontend/src/scenes/settings/project/teamMembersLogic.tsx @@ -1,8 +1,12 @@ -import { kea, path, actions, selectors, listeners, events } from 'kea' -import { loaders } from 'kea-loaders' +import { actions, events, kea, listeners, path, selectors } from 'kea' import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' import api from 'lib/api' import { OrganizationMembershipLevel, TeamMembershipLevel } from 'lib/constants' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { membershipLevelToName } from 'lib/utils/permissioning' +import { membersLogic } from 'scenes/organization/membersLogic' + import { AvailableFeature, BaseMemberType, @@ -12,12 +16,10 @@ import { UserBasicType, UserType, } from '~/types' -import type { teamMembersLogicType } from './teamMembersLogicType' -import { membershipLevelToName } from 'lib/utils/permissioning' -import { userLogic } from '../../userLogic' + import { teamLogic } from '../../teamLogic' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { membersLogic } from 'scenes/organization/membersLogic' +import { userLogic } from '../../userLogic' +import type { teamMembersLogicType } from './teamMembersLogicType' export const MINIMUM_IMPLICIT_ACCESS_LEVEL = OrganizationMembershipLevel.Admin diff --git a/frontend/src/scenes/settings/project/webhookIntegrationLogic.ts b/frontend/src/scenes/settings/project/webhookIntegrationLogic.ts index f649190f541c6..b9df2b8de3b72 100644 --- a/frontend/src/scenes/settings/project/webhookIntegrationLogic.ts +++ b/frontend/src/scenes/settings/project/webhookIntegrationLogic.ts @@ -1,9 +1,10 @@ +import { kea, listeners, path, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, selectors, listeners } from 'kea' import api from 'lib/api' import { lemonToast } from 'lib/lemon-ui/lemonToast' import { capitalizeFirstLetter } from 'lib/utils' import { teamLogic } from 'scenes/teamLogic' + import type { webhookIntegrationLogicType } from './webhookIntegrationLogicType' function adjustDiscordWebhook(webhookUrl: string): string { diff --git a/frontend/src/scenes/settings/settingsLogic.ts b/frontend/src/scenes/settings/settingsLogic.ts index 3b72938fd742c..8af9bb55f8475 100644 --- a/frontend/src/scenes/settings/settingsLogic.ts +++ b/frontend/src/scenes/settings/settingsLogic.ts @@ -1,13 +1,12 @@ import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' -import { SettingsMap } from './SettingsMap' - -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { FEATURE_FLAGS } from 'lib/constants' -import { SettingSection, Setting, SettingSectionId, SettingLevelId, SettingId, SettingsLogicProps } from './types' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { urls } from 'scenes/urls' import type { settingsLogicType } from './settingsLogicType' -import { urls } from 'scenes/urls' -import { copyToClipboard } from 'lib/utils/copyToClipboard' +import { SettingsMap } from './SettingsMap' +import { Setting, SettingId, SettingLevelId, SettingSection, SettingSectionId, SettingsLogicProps } from './types' export const settingsLogic = kea([ props({} as SettingsLogicProps), diff --git a/frontend/src/scenes/settings/settingsSceneLogic.ts b/frontend/src/scenes/settings/settingsSceneLogic.ts index 5fcc8bb4b9b18..9339ee8b1274e 100644 --- a/frontend/src/scenes/settings/settingsSceneLogic.ts +++ b/frontend/src/scenes/settings/settingsSceneLogic.ts @@ -1,16 +1,16 @@ import { connect, kea, path, selectors } from 'kea' -import { SettingsMap } from './SettingsMap' - -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { actionToUrl, urlToAction } from 'kea-router' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { capitalizeFirstLetter } from 'lib/utils' +import { Scene } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + import { Breadcrumb } from '~/types' -import { capitalizeFirstLetter } from 'lib/utils' -import { SettingSectionId, SettingLevelId, SettingLevelIds } from './types' -import type { settingsSceneLogicType } from './settingsSceneLogicType' import { settingsLogic } from './settingsLogic' -import { Scene } from 'scenes/sceneTypes' +import { SettingsMap } from './SettingsMap' +import type { settingsSceneLogicType } from './settingsSceneLogicType' +import { SettingLevelId, SettingLevelIds, SettingSectionId } from './types' export const settingsSceneLogic = kea([ path(['scenes', 'settings', 'settingsSceneLogic']), diff --git a/frontend/src/scenes/settings/user/ChangePassword.tsx b/frontend/src/scenes/settings/user/ChangePassword.tsx index b7cb1139c40b4..89007c6ecdd5d 100644 --- a/frontend/src/scenes/settings/user/ChangePassword.tsx +++ b/frontend/src/scenes/settings/user/ChangePassword.tsx @@ -1,8 +1,9 @@ +import { LemonButton, LemonInput } from '@posthog/lemon-ui' import { useValues } from 'kea' import { Form } from 'kea-forms' -import { Field } from 'lib/forms/Field' -import { LemonButton, LemonInput } from '@posthog/lemon-ui' import PasswordStrength from 'lib/components/PasswordStrength' +import { Field } from 'lib/forms/Field' + import { changePasswordLogic } from './changePasswordLogic' export function ChangePassword(): JSX.Element { diff --git a/frontend/src/scenes/settings/user/OptOutCapture.tsx b/frontend/src/scenes/settings/user/OptOutCapture.tsx index 4290928ebb5d1..da5bb75147877 100644 --- a/frontend/src/scenes/settings/user/OptOutCapture.tsx +++ b/frontend/src/scenes/settings/user/OptOutCapture.tsx @@ -1,6 +1,6 @@ +import { LemonSwitch } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { userLogic } from 'scenes/userLogic' -import { LemonSwitch } from '@posthog/lemon-ui' export function OptOutCapture(): JSX.Element { const { user, userLoading } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/user/PersonalAPIKeys.tsx b/frontend/src/scenes/settings/user/PersonalAPIKeys.tsx index aca059f1e1fd5..a618450c8de94 100644 --- a/frontend/src/scenes/settings/user/PersonalAPIKeys.tsx +++ b/frontend/src/scenes/settings/user/PersonalAPIKeys.tsx @@ -1,13 +1,15 @@ -import { useState, useCallback, Dispatch, SetStateAction, useEffect } from 'react' +import { LemonDialog, LemonInput, LemonModal, LemonTable, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { personalAPIKeysLogic } from './personalAPIKeysLogic' -import { PersonalAPIKeyType } from '~/types' +import { IconPlus } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { humanFriendlyDetailedTime } from 'lib/utils' +import { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react' + +import { PersonalAPIKeyType } from '~/types' + import { CopyToClipboardInline } from '../../../lib/components/CopyToClipboard' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { LemonDialog, LemonInput, LemonModal, LemonTable, Link } from '@posthog/lemon-ui' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { IconPlus } from 'lib/lemon-ui/icons' +import { personalAPIKeysLogic } from './personalAPIKeysLogic' function CreateKeyModal({ isOpen, diff --git a/frontend/src/scenes/settings/user/TwoFactorAuthentication.tsx b/frontend/src/scenes/settings/user/TwoFactorAuthentication.tsx index a7d71f1bb3d74..4d6333b16fbfb 100644 --- a/frontend/src/scenes/settings/user/TwoFactorAuthentication.tsx +++ b/frontend/src/scenes/settings/user/TwoFactorAuthentication.tsx @@ -1,10 +1,10 @@ -import { useValues, useActions } from 'kea' -import { userLogic } from 'scenes/userLogic' import { LemonButton, LemonModal } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' import { IconCheckmark, IconWarning } from 'lib/lemon-ui/icons' import { useState } from 'react' import { Setup2FA } from 'scenes/authentication/Setup2FA' import { membersLogic } from 'scenes/organization/membersLogic' +import { userLogic } from 'scenes/userLogic' export function TwoFactorAuthentication(): JSX.Element { const { user } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/user/UpdateEmailPreferences.tsx b/frontend/src/scenes/settings/user/UpdateEmailPreferences.tsx index 5d1387d376324..d9d9abfc98f20 100644 --- a/frontend/src/scenes/settings/user/UpdateEmailPreferences.tsx +++ b/frontend/src/scenes/settings/user/UpdateEmailPreferences.tsx @@ -1,6 +1,6 @@ -import { useValues, useActions } from 'kea' -import { userLogic } from 'scenes/userLogic' import { LemonSwitch } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { userLogic } from 'scenes/userLogic' export function UpdateEmailPreferences(): JSX.Element { const { user, userLoading } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/user/UserDetails.tsx b/frontend/src/scenes/settings/user/UserDetails.tsx index 324e70bc2818d..0ad4587948d61 100644 --- a/frontend/src/scenes/settings/user/UserDetails.tsx +++ b/frontend/src/scenes/settings/user/UserDetails.tsx @@ -1,10 +1,10 @@ +import { LemonTag } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { userLogic } from 'scenes/userLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { Form } from 'kea-forms' import { Field } from 'lib/forms/Field' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { Form } from 'kea-forms' -import { LemonTag } from '@posthog/lemon-ui' +import { userLogic } from 'scenes/userLogic' export function UserDetails(): JSX.Element { const { userLoading, isUserDetailsSubmitting, userDetailsChanged, user } = useValues(userLogic) diff --git a/frontend/src/scenes/settings/user/changePasswordLogic.ts b/frontend/src/scenes/settings/user/changePasswordLogic.ts index 99053bdf8da10..4b04c0b7452aa 100644 --- a/frontend/src/scenes/settings/user/changePasswordLogic.ts +++ b/frontend/src/scenes/settings/user/changePasswordLogic.ts @@ -1,5 +1,5 @@ import { lemonToast } from '@posthog/lemon-ui' -import { kea, path, connect } from 'kea' +import { connect, kea, path } from 'kea' import { forms } from 'kea-forms' import api from 'lib/api' import { userLogic } from 'scenes/userLogic' diff --git a/frontend/src/scenes/settings/user/personalAPIKeysLogic.ts b/frontend/src/scenes/settings/user/personalAPIKeysLogic.ts index 4097d6997f895..7b04396e5079d 100644 --- a/frontend/src/scenes/settings/user/personalAPIKeysLogic.ts +++ b/frontend/src/scenes/settings/user/personalAPIKeysLogic.ts @@ -1,10 +1,12 @@ +import { kea, listeners, path } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, listeners } from 'kea' import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { copyToClipboard } from 'lib/utils/copyToClipboard' + import { PersonalAPIKeyType } from '~/types' + import type { personalAPIKeysLogicType } from './personalAPIKeysLogicType' -import { copyToClipboard } from 'lib/utils/copyToClipboard' -import { lemonToast } from 'lib/lemon-ui/lemonToast' export const personalAPIKeysLogic = kea([ path(['lib', 'components', 'PersonalAPIKeys', 'personalAPIKeysLogic']), diff --git a/frontend/src/scenes/sites/Site.tsx b/frontend/src/scenes/sites/Site.tsx index ad487e77da371..4e97669011639 100644 --- a/frontend/src/scenes/sites/Site.tsx +++ b/frontend/src/scenes/sites/Site.tsx @@ -1,8 +1,10 @@ -import { SceneExport } from 'scenes/sceneTypes' import './Site.scss' -import { SiteLogicProps, siteLogic } from './siteLogic' -import { AuthorizedUrlListType, authorizedUrlListLogic } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' + import { useValues } from 'kea' +import { authorizedUrlListLogic, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { SceneExport } from 'scenes/sceneTypes' + +import { siteLogic, SiteLogicProps } from './siteLogic' export const scene: SceneExport = { component: Site, diff --git a/frontend/src/scenes/sites/siteLogic.ts b/frontend/src/scenes/sites/siteLogic.ts index 06ad0e13fdf98..48a318edde6b1 100644 --- a/frontend/src/scenes/sites/siteLogic.ts +++ b/frontend/src/scenes/sites/siteLogic.ts @@ -1,8 +1,9 @@ -import { kea, props, selectors, path } from 'kea' +import { kea, path, props, selectors } from 'kea' +import { Scene } from 'scenes/sceneTypes' + import { Breadcrumb } from '~/types' import type { siteLogicType } from './siteLogicType' -import { Scene } from 'scenes/sceneTypes' export interface SiteLogicProps { url: string diff --git a/frontend/src/scenes/surveys/Survey.tsx b/frontend/src/scenes/surveys/Survey.tsx index 30ed3fe5e4e34..12bebeb2a9c4a 100644 --- a/frontend/src/scenes/surveys/Survey.tsx +++ b/frontend/src/scenes/surveys/Survey.tsx @@ -1,20 +1,22 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { surveyLogic } from './surveyLogic' +import { LemonButton, LemonDivider, Link } from '@posthog/lemon-ui' import { BindLogic, useActions, useValues } from 'kea' import { Form } from 'kea-forms' +import { router } from 'kea-router' +import { FlagSelector } from 'lib/components/FlagSelector' +import { NotFound } from 'lib/components/NotFound' import { PageHeader } from 'lib/components/PageHeader' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' -import { LemonButton, LemonDivider, Link } from '@posthog/lemon-ui' -import { router } from 'kea-router' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' +import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + import { Survey, SurveyUrlMatchType } from '~/types' -import { SurveyView } from './SurveyView' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' + import { NewSurvey, SurveyUrlMatchTypeLabels } from './constants' -import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' import SurveyEdit from './SurveyEdit' -import { NotFound } from 'lib/components/NotFound' -import { FlagSelector } from 'lib/components/FlagSelector' +import { surveyLogic } from './surveyLogic' +import { SurveyView } from './SurveyView' export const scene: SceneExport = { component: SurveyComponent, diff --git a/frontend/src/scenes/surveys/SurveyAPIEditor.tsx b/frontend/src/scenes/surveys/SurveyAPIEditor.tsx index c474d973db1a9..f0c2b9190f77b 100644 --- a/frontend/src/scenes/surveys/SurveyAPIEditor.tsx +++ b/frontend/src/scenes/surveys/SurveyAPIEditor.tsx @@ -1,6 +1,8 @@ +import { CodeSnippet, Language } from 'lib/components/CodeSnippet' + import { Survey } from '~/types' + import { NewSurvey } from './constants' -import { CodeSnippet, Language } from 'lib/components/CodeSnippet' export function SurveyAPIEditor({ survey }: { survey: Survey | NewSurvey }): JSX.Element { // Make sure this is synced to SurveyAPISerializer diff --git a/frontend/src/scenes/surveys/SurveyAppearance.tsx b/frontend/src/scenes/surveys/SurveyAppearance.tsx index f4227ef131cc0..c98574af938d9 100644 --- a/frontend/src/scenes/surveys/SurveyAppearance.tsx +++ b/frontend/src/scenes/surveys/SurveyAppearance.tsx @@ -1,15 +1,21 @@ import './SurveyAppearance.scss' + import { LemonButton, LemonCheckbox, LemonInput, Link } from '@posthog/lemon-ui' +import { useValues } from 'kea' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import React, { useEffect, useRef, useState } from 'react' + import { - SurveyAppearance as SurveyAppearanceType, - SurveyQuestion, - RatingSurveyQuestion, - SurveyQuestionType, - MultipleSurveyQuestion, AvailableFeature, BasicSurveyQuestion, LinkSurveyQuestion, + MultipleSurveyQuestion, + RatingSurveyQuestion, + SurveyAppearance as SurveyAppearanceType, + SurveyQuestion, + SurveyQuestionType, } from '~/types' + import { defaultSurveyAppearance } from './constants' import { cancel, @@ -23,9 +29,6 @@ import { verySatisfiedEmoji, } from './SurveyAppearanceUtils' import { surveysLogic } from './surveysLogic' -import { useValues } from 'kea' -import React, { useEffect, useRef, useState } from 'react' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' import { sanitizeHTML } from './utils' interface SurveyAppearanceProps { diff --git a/frontend/src/scenes/surveys/SurveyEdit.tsx b/frontend/src/scenes/surveys/SurveyEdit.tsx index 2941e0a5576fd..64d2e0aad64ae 100644 --- a/frontend/src/scenes/surveys/SurveyEdit.tsx +++ b/frontend/src/scenes/surveys/SurveyEdit.tsx @@ -1,7 +1,5 @@ import './EditSurvey.scss' -import { SurveyEditSection, surveyLogic } from './surveyLogic' -import { BindLogic, useActions, useValues } from 'kea' -import { Group } from 'kea-forms' + import { LemonBanner, LemonButton, @@ -14,17 +12,37 @@ import { LemonTextArea, Link, } from '@posthog/lemon-ui' +import clsx from 'clsx' +import { BindLogic, useActions, useValues } from 'kea' +import { Group } from 'kea-forms' +import { CodeEditor } from 'lib/components/CodeEditors' +import { FlagSelector } from 'lib/components/FlagSelector' +import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { FEATURE_FLAGS } from 'lib/constants' import { Field, PureField } from 'lib/forms/Field' +import { IconCancel, IconDelete, IconLock, IconPlus, IconPlusMini } from 'lib/lemon-ui/icons' +import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic' +import React from 'react' +import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' +import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' + import { + AvailableFeature, + LinkSurveyQuestion, + RatingSurveyQuestion, SurveyQuestion, SurveyQuestionType, SurveyType, - LinkSurveyQuestion, - RatingSurveyQuestion, SurveyUrlMatchType, - AvailableFeature, } from '~/types' -import { IconCancel, IconDelete, IconLock, IconPlus, IconPlusMini } from 'lib/lemon-ui/icons' + +import { + defaultSurveyAppearance, + defaultSurveyFieldValues, + SurveyQuestionLabel, + SurveyUrlMatchTypeLabels, +} from './constants' +import { SurveyAPIEditor } from './SurveyAPIEditor' import { BaseAppearance, Customization, @@ -32,24 +50,9 @@ import { SurveyMultipleChoiceAppearance, SurveyRatingAppearance, } from './SurveyAppearance' -import { SurveyAPIEditor } from './SurveyAPIEditor' -import { featureFlagLogic } from 'scenes/feature-flags/featureFlagLogic' -import { - defaultSurveyFieldValues, - defaultSurveyAppearance, - SurveyQuestionLabel, - SurveyUrlMatchTypeLabels, -} from './constants' -import { FeatureFlagReleaseConditions } from 'scenes/feature-flags/FeatureFlagReleaseConditions' -import React from 'react' -import { CodeEditor } from 'lib/components/CodeEditors' -import { FEATURE_FLAGS } from 'lib/constants' -import { featureFlagLogic as enabledFeaturesLogic } from 'lib/logic/featureFlagLogic' import { SurveyFormAppearance } from './SurveyFormAppearance' -import { PayGateMini } from 'lib/components/PayGateMini/PayGateMini' +import { SurveyEditSection, surveyLogic } from './surveyLogic' import { surveysLogic } from './surveysLogic' -import { FlagSelector } from 'lib/components/FlagSelector' -import clsx from 'clsx' function PresentationTypeCard({ title, diff --git a/frontend/src/scenes/surveys/SurveyFormAppearance.tsx b/frontend/src/scenes/surveys/SurveyFormAppearance.tsx index 2ca9b4d98dae5..1c6eb32739283 100644 --- a/frontend/src/scenes/surveys/SurveyFormAppearance.tsx +++ b/frontend/src/scenes/surveys/SurveyFormAppearance.tsx @@ -1,9 +1,11 @@ import { LemonSelect } from '@posthog/lemon-ui' -import { SurveyAppearance, SurveyThankYou } from './SurveyAppearance' -import { SurveyAPIEditor } from './SurveyAPIEditor' -import { NewSurvey, defaultSurveyAppearance } from './constants' + import { Survey, SurveyType } from '~/types' +import { defaultSurveyAppearance, NewSurvey } from './constants' +import { SurveyAPIEditor } from './SurveyAPIEditor' +import { SurveyAppearance, SurveyThankYou } from './SurveyAppearance' + interface SurveyFormAppearanceProps { activePreview: number survey: NewSurvey | Survey diff --git a/frontend/src/scenes/surveys/SurveySettings.tsx b/frontend/src/scenes/surveys/SurveySettings.tsx index 6278a1530ce52..ac5fd8c458be4 100644 --- a/frontend/src/scenes/surveys/SurveySettings.tsx +++ b/frontend/src/scenes/surveys/SurveySettings.tsx @@ -1,8 +1,8 @@ +import { LemonSwitch, Link } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { LemonDialog } from 'lib/lemon-ui/LemonDialog' import { teamLogic } from 'scenes/teamLogic' -import { LemonSwitch, Link } from '@posthog/lemon-ui' import { urls } from 'scenes/urls' -import { LemonDialog } from 'lib/lemon-ui/LemonDialog' export type SurveySettingsProps = { inModal?: boolean diff --git a/frontend/src/scenes/surveys/SurveyTemplates.tsx b/frontend/src/scenes/surveys/SurveyTemplates.tsx index 759245b2f174e..fb2bbf55290de 100644 --- a/frontend/src/scenes/surveys/SurveyTemplates.tsx +++ b/frontend/src/scenes/surveys/SurveyTemplates.tsx @@ -1,14 +1,17 @@ -import { SceneExport } from 'scenes/sceneTypes' -import { SurveyAppearance } from './SurveyAppearance' -import { defaultSurveyTemplates, defaultSurveyAppearance } from './constants' -import { SurveyQuestion } from '~/types' import './SurveyTemplates.scss' + +import { LemonButton } from '@posthog/lemon-ui' import { useActions } from 'kea' import { PageHeader } from 'lib/components/PageHeader' -import { LemonButton } from '@posthog/lemon-ui' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { SceneExport } from 'scenes/sceneTypes' import { urls } from 'scenes/urls' + +import { SurveyQuestion } from '~/types' + +import { defaultSurveyAppearance, defaultSurveyTemplates } from './constants' +import { SurveyAppearance } from './SurveyAppearance' import { surveyLogic } from './surveyLogic' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' export const scene: SceneExport = { component: SurveyTemplates, diff --git a/frontend/src/scenes/surveys/SurveyView.scss b/frontend/src/scenes/surveys/SurveyView.scss index 40c0cf169a8ab..62ae32fea2cc8 100644 --- a/frontend/src/scenes/surveys/SurveyView.scss +++ b/frontend/src/scenes/surveys/SurveyView.scss @@ -17,8 +17,3 @@ break-inside: avoid; box-sizing: border-box; } - -.masonry-item-text { - max-height: 305px; - overflow-y: scroll; -} diff --git a/frontend/src/scenes/surveys/SurveyView.tsx b/frontend/src/scenes/surveys/SurveyView.tsx index 334027e67ba0d..9ac71df5662be 100644 --- a/frontend/src/scenes/surveys/SurveyView.tsx +++ b/frontend/src/scenes/surveys/SurveyView.tsx @@ -1,33 +1,36 @@ +import './SurveyView.scss' + import { TZLabel } from '@posthog/apps-common' import { LemonButton, LemonDivider, Link } from '@posthog/lemon-ui' -import { useValues, useActions } from 'kea' +import { useActions, useValues } from 'kea' import { EditableField } from 'lib/components/EditableField/EditableField' +import { PageHeader } from 'lib/components/PageHeader' +import { FEATURE_FLAGS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' import { More } from 'lib/lemon-ui/LemonButton/More' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { capitalizeFirstLetter, pluralize } from 'lib/utils' -import { useState, useEffect } from 'react' +import { useEffect, useState } from 'react' + import { Query } from '~/queries/Query/Query' -import { surveyLogic } from './surveyLogic' -import { surveysLogic } from './surveysLogic' -import { PageHeader } from 'lib/components/PageHeader' -import { SurveyReleaseSummary } from './Survey' -import { PropertyFilterType, PropertyOperator, Survey, SurveyQuestionType, SurveyType } from '~/types' -import { SurveyAPIEditor } from './SurveyAPIEditor' import { NodeKind } from '~/queries/schema' -import { dayjs } from 'lib/dayjs' +import { PropertyFilterType, PropertyOperator, Survey, SurveyQuestionType, SurveyType } from '~/types' + import { SURVEY_EVENT_NAME } from './constants' -import { FEATURE_FLAGS } from 'lib/constants' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { SurveyReleaseSummary } from './Survey' +import { SurveyAPIEditor } from './SurveyAPIEditor' +import { SurveyFormAppearance } from './SurveyFormAppearance' +import { surveyLogic } from './surveyLogic' +import { surveysLogic } from './surveysLogic' import { - Summary, - RatingQuestionBarChart, - SingleChoiceQuestionPieChart, MultipleChoiceQuestionBarChart, OpenTextViz, + RatingQuestionBarChart, + SingleChoiceQuestionPieChart, + Summary, } from './surveyViewViz' -import './SurveyView.scss' -import { SurveyFormAppearance } from './SurveyFormAppearance' export function SurveyView({ id }: { id: string }): JSX.Element { const { survey, surveyLoading, selectedQuestion } = useValues(surveyLogic) diff --git a/frontend/src/scenes/surveys/Surveys.stories.tsx b/frontend/src/scenes/surveys/Surveys.stories.tsx index 6caf5cba7f50a..560897857a549 100644 --- a/frontend/src/scenes/surveys/Surveys.stories.tsx +++ b/frontend/src/scenes/surveys/Surveys.stories.tsx @@ -1,6 +1,9 @@ +import { Meta, StoryFn } from '@storybook/react' +import { router } from 'kea-router' import { useEffect } from 'react' import { App } from 'scenes/App' import { urls } from 'scenes/urls' + import { mswDecorator } from '~/mocks/browser' import { toPaginatedResponse } from '~/mocks/handlers' import { @@ -11,8 +14,7 @@ import { SurveyQuestionType, SurveyType, } from '~/types' -import { Meta, StoryFn } from '@storybook/react' -import { router } from 'kea-router' + import { SurveyEditSection, surveyLogic } from './surveyLogic' const MOCK_BASIC_SURVEY: Survey = { @@ -243,11 +245,7 @@ export const SurveyView: StoryFn = () => { }, []) return } -SurveyView.parameters = { - testOptions: { - skip: true, // FIXME: Fix the mocked data so that survey results can actually load - }, -} +SurveyView.tags = ['test-skip'] // FIXME: Fix the mocked data so that survey results can actually load export const SurveyTemplates: StoryFn = () => { useEffect(() => { diff --git a/frontend/src/scenes/surveys/Surveys.tsx b/frontend/src/scenes/surveys/Surveys.tsx index 143f39fcf01e7..877895ccb38a2 100644 --- a/frontend/src/scenes/surveys/Surveys.tsx +++ b/frontend/src/scenes/surveys/Surveys.tsx @@ -1,36 +1,38 @@ import { LemonButton, + LemonButtonWithSideAction, LemonDivider, LemonInput, LemonSelect, LemonTable, - Link, LemonTag, LemonTagType, + Link, Spinner, - LemonButtonWithSideAction, } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { router } from 'kea-router' import { PageHeader } from 'lib/components/PageHeader' +import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner' +import { dayjs } from 'lib/dayjs' +import { IconSettings } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { More } from 'lib/lemon-ui/LemonButton/More' -import stringWithWBR from 'lib/utils/stringWithWBR' -import { SceneExport } from 'scenes/sceneTypes' -import { urls } from 'scenes/urls' -import { getSurveyStatus, surveysLogic } from './surveysLogic' -import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' -import { ProductKey, ProgressStatus, Survey } from '~/types' import { LemonTableColumn } from 'lib/lemon-ui/LemonTable' -import { useActions, useValues } from 'kea' -import { router } from 'kea-router' +import { createdAtColumn, createdByColumn } from 'lib/lemon-ui/LemonTable/columnUtils' import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import stringWithWBR from 'lib/utils/stringWithWBR' import { useState } from 'react' -import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' import { userLogic } from 'scenes/userLogic' -import { dayjs } from 'lib/dayjs' -import { VersionCheckerBanner } from 'lib/components/VersionChecker/VersionCheckerBanner' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { IconSettings } from 'lib/lemon-ui/icons' -import { openSurveysSettingsDialog } from './SurveySettings' + +import { ProductKey, ProgressStatus, Survey } from '~/types' + import { SurveyQuestionLabel } from './constants' +import { openSurveysSettingsDialog } from './SurveySettings' +import { getSurveyStatus, surveysLogic } from './surveysLogic' export const scene: SceneExport = { component: Surveys, @@ -117,7 +119,7 @@ export function Surveys(): JSX.Element { ]} />
    - + {showSurveysDisabledBanner ? ( -
    - {JSON.stringify(event.properties[surveyResponseField])} +
    + {typeof event.properties[surveyResponseField] !== 'string' + ? JSON.stringify(event.properties[surveyResponseField]) + : event.properties[surveyResponseField]}
    { if (attr === 'slack_incoming_webhook') { diff --git a/frontend/src/scenes/toolbar-launch/ToolbarLaunch.tsx b/frontend/src/scenes/toolbar-launch/ToolbarLaunch.tsx index e844b3d9ab9ec..a3551ee84d6d0 100644 --- a/frontend/src/scenes/toolbar-launch/ToolbarLaunch.tsx +++ b/frontend/src/scenes/toolbar-launch/ToolbarLaunch.tsx @@ -1,12 +1,13 @@ -import { PageHeader } from 'lib/components/PageHeader' -import { SceneExport } from 'scenes/sceneTypes' import './ToolbarLaunch.scss' -import { Link } from 'lib/lemon-ui/Link' -import { urls } from 'scenes/urls' -import { IconFlag, IconGroupedEvents, IconHeatmap, IconMagnifier } from 'lib/lemon-ui/icons' + import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' +import { PageHeader } from 'lib/components/PageHeader' +import { IconFlag, IconGroupedEvents, IconHeatmap, IconMagnifier } from 'lib/lemon-ui/icons' import { LemonDivider } from 'lib/lemon-ui/LemonDivider' +import { Link } from 'lib/lemon-ui/Link' +import { SceneExport } from 'scenes/sceneTypes' +import { urls } from 'scenes/urls' export const scene: SceneExport = { component: ToolbarLaunch, diff --git a/frontend/src/scenes/trends/Trends.tsx b/frontend/src/scenes/trends/Trends.tsx index 4e2cb0879ee83..c6e7faffcf6b2 100644 --- a/frontend/src/scenes/trends/Trends.tsx +++ b/frontend/src/scenes/trends/Trends.tsx @@ -1,14 +1,16 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' -import { ActionsPie, ActionsLineGraph, ActionsHorizontalBar } from './viz' -import { ChartDisplayType, InsightType, ItemMode } from '~/types' -import { InsightsTable } from 'scenes/insights/views/InsightsTable/InsightsTable' import { insightLogic } from 'scenes/insights/insightLogic' import { insightSceneLogic } from 'scenes/insights/insightSceneLogic' -import { WorldMap } from 'scenes/insights/views/WorldMap' import { BoldNumber } from 'scenes/insights/views/BoldNumber' -import { LemonButton } from '@posthog/lemon-ui' -import { trendsDataLogic } from './trendsDataLogic' +import { InsightsTable } from 'scenes/insights/views/InsightsTable/InsightsTable' +import { WorldMap } from 'scenes/insights/views/WorldMap' + import { QueryContext } from '~/queries/types' +import { ChartDisplayType, InsightType, ItemMode } from '~/types' + +import { trendsDataLogic } from './trendsDataLogic' +import { ActionsHorizontalBar, ActionsLineGraph, ActionsPie } from './viz' interface Props { view: InsightType diff --git a/frontend/src/scenes/trends/mathsLogic.tsx b/frontend/src/scenes/trends/mathsLogic.tsx index 4d107d9349d90..f8fe2e6fdd56b 100644 --- a/frontend/src/scenes/trends/mathsLogic.tsx +++ b/frontend/src/scenes/trends/mathsLogic.tsx @@ -1,8 +1,10 @@ -import { kea, path, connect, selectors } from 'kea' +import { connect, kea, path, selectors } from 'kea' +import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' + import { groupsModel } from '~/models/groupsModel' -import type { mathsLogicType } from './mathsLogicType' import { BaseMathType, CountPerActorMathType, HogQLMathType, PropertyMathType } from '~/types' -import { groupsAccessLogic } from 'lib/introductions/groupsAccessLogic' + +import type { mathsLogicType } from './mathsLogicType' export enum MathCategory { EventCount, diff --git a/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx b/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx index 631648849327f..bbb3a85a68ac2 100644 --- a/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx +++ b/frontend/src/scenes/trends/persons-modal/PersonsModal.stories.tsx @@ -1,9 +1,11 @@ import { Meta, Story } from '@storybook/react' import { MOCK_TEAM_ID } from 'lib/api.mock' import { RawPropertiesTimelineResult } from 'lib/components/PropertiesTimeline/propertiesTimelineLogic' + import { useStorybookMocks } from '~/mocks/browser' -import { PersonsModal as PersonsModalComponent } from './PersonsModal' + import EXAMPLE_PERSONS_RESPONSE from './__mocks__/examplePersonsResponse.json' +import { PersonsModal as PersonsModalComponent } from './PersonsModal' const meta: Meta = { title: 'Scenes-App/Persons Modal', diff --git a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx index 2a083508fcd9b..894a03d70b6e0 100644 --- a/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx +++ b/frontend/src/scenes/trends/persons-modal/PersonsModal.tsx @@ -1,5 +1,30 @@ -import { useState } from 'react' +import './PersonsModal.scss' + +import { LemonBadge, LemonButton, LemonDivider, LemonInput, LemonModal, LemonSelect, Link } from '@posthog/lemon-ui' +import { LemonModalProps } from '@posthog/lemon-ui' +import { Skeleton } from 'antd' import { useActions, useValues } from 'kea' +import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' +import { triggerExport } from 'lib/components/ExportButton/exporter' +import { PropertiesTable } from 'lib/components/PropertiesTable' +import { PropertiesTimeline } from 'lib/components/PropertiesTimeline' +import { IconPlayCircle, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' +import { LemonBanner } from 'lib/lemon-ui/LemonBanner' +import { LemonTabs } from 'lib/lemon-ui/LemonTabs' +import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { capitalizeFirstLetter, isGroupType, midEllipsis, pluralize } from 'lib/utils' +import { useState } from 'react' +import { createRoot } from 'react-dom/client' +import { GroupActorDisplay, groupDisplayId } from 'scenes/persons/GroupActorDisplay' +import { asDisplay } from 'scenes/persons/person-utils' +import { PersonDisplay } from 'scenes/persons/PersonDisplay' +import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' +import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { Noun } from '~/models/groupsModel' import { ActorType, ExporterFormat, @@ -7,32 +32,9 @@ import { PropertyDefinitionType, SessionRecordingType, } from '~/types' + import { personsModalLogic } from './personsModalLogic' -import { CopyToClipboardInline } from 'lib/components/CopyToClipboard' -import { capitalizeFirstLetter, isGroupType, midEllipsis, pluralize } from 'lib/utils' -import { GroupActorDisplay, groupDisplayId } from 'scenes/persons/GroupActorDisplay' -import { IconPlayCircle, IconUnfoldLess, IconUnfoldMore } from 'lib/lemon-ui/icons' -import { triggerExport } from 'lib/components/ExportButton/exporter' -import { LemonButton, LemonBadge, LemonDivider, LemonInput, LemonModal, LemonSelect, Link } from '@posthog/lemon-ui' -import { PersonDisplay } from 'scenes/persons/PersonDisplay' -import { createRoot } from 'react-dom/client' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { SaveCohortModal } from './SaveCohortModal' -import { ProfilePicture } from 'lib/lemon-ui/ProfilePicture' -import { Skeleton } from 'antd' -import { SessionPlayerModal } from 'scenes/session-recordings/player/modal/SessionPlayerModal' -import { sessionPlayerModalLogic } from 'scenes/session-recordings/player/modal/sessionPlayerModalLogic' -import { LemonBanner } from 'lib/lemon-ui/LemonBanner' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { Noun } from '~/models/groupsModel' -import { LemonModalProps } from '@posthog/lemon-ui' -import { PropertiesTimeline } from 'lib/components/PropertiesTimeline' -import { PropertiesTable } from 'lib/components/PropertiesTable' -import { teamLogic } from 'scenes/teamLogic' -import { LemonTabs } from 'lib/lemon-ui/LemonTabs' - -import './PersonsModal.scss' -import { asDisplay } from 'scenes/persons/person-utils' export interface PersonsModalProps extends Pick { onAfterClose?: () => void diff --git a/frontend/src/scenes/trends/persons-modal/SaveCohortModal.tsx b/frontend/src/scenes/trends/persons-modal/SaveCohortModal.tsx index f513e1e5e3a2f..7a8da3e4219fc 100644 --- a/frontend/src/scenes/trends/persons-modal/SaveCohortModal.tsx +++ b/frontend/src/scenes/trends/persons-modal/SaveCohortModal.tsx @@ -1,5 +1,5 @@ -import { useState } from 'react' import { LemonButton, LemonInput, LemonModal } from '@posthog/lemon-ui' +import { useState } from 'react' interface Props { onSave: (title: string) => void diff --git a/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts b/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts index 50f3156f013d1..70958019ed94c 100644 --- a/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts +++ b/frontend/src/scenes/trends/persons-modal/peronsModalLogic.test.ts @@ -1,7 +1,9 @@ -import { personsModalLogic } from './personsModalLogic' -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' +import { initKeaTests } from '~/test/init' + +import { personsModalLogic } from './personsModalLogic' + describe('personsModalLogic', () => { let logic: ReturnType diff --git a/frontend/src/scenes/trends/persons-modal/persons-modal-utils.tsx b/frontend/src/scenes/trends/persons-modal/persons-modal-utils.tsx index 4956c5f36c002..3f44300480fe5 100644 --- a/frontend/src/scenes/trends/persons-modal/persons-modal-utils.tsx +++ b/frontend/src/scenes/trends/persons-modal/persons-modal-utils.tsx @@ -1,10 +1,13 @@ import * as Sentry from '@sentry/react' -import md5 from 'md5' +import { getBarColorFromStatus, getSeriesColor } from 'lib/colors' +import { InsightLabel } from 'lib/components/InsightLabel' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' import { dayjs } from 'lib/dayjs' import { capitalizeFirstLetter, pluralize, toParams } from 'lib/utils' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import md5 from 'md5' import { isFunnelsFilter, isPathsFilter } from 'scenes/insights/sharedUtils' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' + import { FunnelsFilterType, FunnelVizType, @@ -13,8 +16,6 @@ import { PathsFilterType, StepOrderValue, } from '~/types' -import { InsightLabel } from 'lib/components/InsightLabel' -import { getBarColorFromStatus, getSeriesColor } from 'lib/colors' export const funnelTitle = (props: { converted: boolean diff --git a/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts b/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts index 1497b84e180fe..2cfc2112c8c80 100644 --- a/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts +++ b/frontend/src/scenes/trends/persons-modal/personsModalLogic.ts @@ -1,17 +1,18 @@ -import { kea, connect, path, props, reducers, actions, selectors, listeners, afterMount } from 'kea' -import api from 'lib/api' -import { ActorType, BreakdownType, ChartDisplayType, IntervalType, PropertiesTimelineFilterType } from '~/types' -import { loaders } from 'kea-loaders' -import { cohortsModel } from '~/models/cohortsModel' import { lemonToast } from '@posthog/lemon-ui' +import { actions, afterMount, connect, kea, listeners, path, props, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { router, urlToAction } from 'kea-router' +import api from 'lib/api' +import { fromParamsGivenUrl, isGroupType } from 'lib/utils' +import { eventUsageLogic } from 'lib/utils/eventUsageLogic' +import { cleanFilters } from 'scenes/insights/utils/cleanFilters' import { urls } from 'scenes/urls' -import type { personsModalLogicType } from './personsModalLogicType' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' -import { fromParamsGivenUrl, isGroupType } from 'lib/utils' +import { cohortsModel } from '~/models/cohortsModel' import { groupsModel } from '~/models/groupsModel' -import { cleanFilters } from 'scenes/insights/utils/cleanFilters' +import { ActorType, BreakdownType, ChartDisplayType, IntervalType, PropertiesTimelineFilterType } from '~/types' + +import type { personsModalLogicType } from './personsModalLogicType' export interface PersonModalLogicProps { url: string diff --git a/frontend/src/scenes/trends/trendsDataLogic.test.ts b/frontend/src/scenes/trends/trendsDataLogic.test.ts index b4e78196b18cd..5a684c1ec1668 100644 --- a/frontend/src/scenes/trends/trendsDataLogic.test.ts +++ b/frontend/src/scenes/trends/trendsDataLogic.test.ts @@ -1,14 +1,14 @@ import { expectLogic } from 'kea-test-utils' -import { initKeaTests } from '~/test/init' - -import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { insightDataLogic } from 'scenes/insights/insightDataLogic' import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { trendsDataLogic } from './trendsDataLogic' -import { ChartDisplayType, InsightLogicProps, InsightModel } from '~/types' +import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic' import { DataNode, LifecycleQuery, NodeKind, TrendsQuery } from '~/queries/schema' -import { trendResult, trendPieResult, lifecycleResult } from './__mocks__/trendsDataLogicMocks' +import { initKeaTests } from '~/test/init' +import { ChartDisplayType, InsightLogicProps, InsightModel } from '~/types' + +import { lifecycleResult, trendPieResult, trendResult } from './__mocks__/trendsDataLogicMocks' +import { trendsDataLogic } from './trendsDataLogic' let logic: ReturnType let builtDataNodeLogic: ReturnType diff --git a/frontend/src/scenes/trends/trendsDataLogic.ts b/frontend/src/scenes/trends/trendsDataLogic.ts index 3c4571cbbdbe8..96cc366507357 100644 --- a/frontend/src/scenes/trends/trendsDataLogic.ts +++ b/frontend/src/scenes/trends/trendsDataLogic.ts @@ -1,12 +1,13 @@ -import { kea, props, key, path, connect, selectors, actions, reducers, listeners } from 'kea' -import { ChartDisplayType, InsightLogicProps, LifecycleToggle, TrendAPIResponse, TrendResult } from '~/types' -import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' +import { actions, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' import api from 'lib/api' +import { dayjs } from 'lib/dayjs' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' +import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' + +import { ChartDisplayType, InsightLogicProps, LifecycleToggle, TrendAPIResponse, TrendResult } from '~/types' import type { trendsDataLogicType } from './trendsDataLogicType' import { IndexedTrendResult } from './types' -import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' -import { dayjs } from 'lib/dayjs' export const trendsDataLogic = kea([ props({} as InsightLogicProps), @@ -26,6 +27,7 @@ export const trendsDataLogic = kea([ 'interval', 'breakdown', 'showValueOnSeries', + 'showLabelOnSeries', 'showPercentStackView', 'supportsPercentStackView', 'trendsFilter', @@ -36,6 +38,7 @@ export const trendsDataLogic = kea([ 'isNonTimeSeriesDisplay', 'isSingleSeries', 'hasLegend', + 'vizSpecificOptions', ], ], actions: [insightVizDataLogic(props), ['setInsightData', 'updateInsightFilter']], @@ -55,7 +58,7 @@ export const trendsDataLogic = kea([ ], }), - selectors({ + selectors(({ values }) => ({ results: [ (s) => [s.insightData], (insightData: TrendAPIResponse | null): TrendResult[] => { @@ -129,7 +132,12 @@ export const trendsDataLogic = kea([ } }, ], - }), + + pieChartVizOptions: [ + () => [() => values.vizSpecificOptions], + (vizSpecificOptions) => vizSpecificOptions?.[ChartDisplayType.ActionsPie], + ], + })), listeners(({ actions, values }) => ({ loadMoreBreakdownValues: async () => { diff --git a/frontend/src/scenes/trends/viz/ActionsHorizontalBar.tsx b/frontend/src/scenes/trends/viz/ActionsHorizontalBar.tsx index dfd1ce105f617..a1478f30142ce 100644 --- a/frontend/src/scenes/trends/viz/ActionsHorizontalBar.tsx +++ b/frontend/src/scenes/trends/viz/ActionsHorizontalBar.tsx @@ -1,16 +1,18 @@ -import { useState, useEffect } from 'react' -import { LineGraph } from '../../insights/views/LineGraph/LineGraph' -import { getSeriesColor } from 'lib/colors' import { useValues } from 'kea' -import { InsightEmptyState } from '../../insights/EmptyStates' -import { ChartParams, GraphType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { openPersonsModal } from '../persons-modal/PersonsModal' +import { getSeriesColor } from 'lib/colors' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { useEffect, useState } from 'react' +import { insightLogic } from 'scenes/insights/insightLogic' +import { formatBreakdownLabel } from 'scenes/insights/utils' + import { cohortsModel } from '~/models/cohortsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatBreakdownLabel } from 'scenes/insights/utils' +import { ChartParams, GraphType } from '~/types' + +import { InsightEmptyState } from '../../insights/EmptyStates' +import { LineGraph } from '../../insights/views/LineGraph/LineGraph' +import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { openPersonsModal } from '../persons-modal/PersonsModal' import { trendsDataLogic } from '../trendsDataLogic' type DataSet = any diff --git a/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx b/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx index 9fe2c882f8dd1..d973981d09a14 100644 --- a/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx +++ b/frontend/src/scenes/trends/viz/ActionsLineGraph.tsx @@ -1,21 +1,23 @@ -import { LineGraph } from '../../insights/views/LineGraph/LineGraph' import { useValues } from 'kea' -import { InsightEmptyState } from '../../insights/EmptyStates' -import { ChartDisplayType, ChartParams, GraphType } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { capitalizeFirstLetter, isMultiSeriesFormula } from 'lib/utils' -import { openPersonsModal } from '../persons-modal/PersonsModal' -import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { combineUrl, router } from 'kea-router' import { DateDisplay } from 'lib/components/DateDisplay' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { trendsDataLogic } from '../trendsDataLogic' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { capitalizeFirstLetter, isMultiSeriesFormula } from 'lib/utils' import { insightDataLogic } from 'scenes/insights/insightDataLogic' -import { isInsightVizNode, isLifecycleQuery } from '~/queries/utils' -import { DataTableNode, NodeKind } from '~/queries/schema' -import { combineUrl, router } from 'kea-router' +import { insightLogic } from 'scenes/insights/insightLogic' import { urls } from 'scenes/urls' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' + +import { DataTableNode, NodeKind } from '~/queries/schema' +import { isInsightVizNode, isLifecycleQuery } from '~/queries/utils' +import { ChartDisplayType, ChartParams, GraphType } from '~/types' + +import { InsightEmptyState } from '../../insights/EmptyStates' +import { LineGraph } from '../../insights/views/LineGraph/LineGraph' +import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { openPersonsModal } from '../persons-modal/PersonsModal' +import { trendsDataLogic } from '../trendsDataLogic' export function ActionsLineGraph({ inSharedMode = false, diff --git a/frontend/src/scenes/trends/viz/ActionsPie.tsx b/frontend/src/scenes/trends/viz/ActionsPie.tsx index 286f76650c55b..d7cd6331c3e74 100644 --- a/frontend/src/scenes/trends/viz/ActionsPie.tsx +++ b/frontend/src/scenes/trends/viz/ActionsPie.tsx @@ -1,21 +1,29 @@ import './ActionsPie.scss' -import { useState, useEffect } from 'react' -import { getSeriesColor } from 'lib/colors' + import { useValues } from 'kea' -import { ChartParams, GraphType, GraphDataset } from '~/types' -import { insightLogic } from 'scenes/insights/insightLogic' -import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' -import { openPersonsModal } from '../persons-modal/PersonsModal' +import { getSeriesColor } from 'lib/colors' +import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend' import { PropertyKeyInfo } from 'lib/components/PropertyKeyInfo' -import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { useEffect, useState } from 'react' +import { formatAggregationAxisValue } from 'scenes/insights/aggregationAxisFormat' +import { insightLogic } from 'scenes/insights/insightLogic' +import { formatBreakdownLabel } from 'scenes/insights/utils' import { PieChart } from 'scenes/insights/views/LineGraph/PieChart' -import { InsightLegend } from 'lib/components/InsightLegend/InsightLegend' + import { cohortsModel } from '~/models/cohortsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { formatBreakdownLabel } from 'scenes/insights/utils' +import { ChartDisplayType, ChartParams, GraphDataset, GraphType } from '~/types' + +import { urlsForDatasets } from '../persons-modal/persons-modal-utils' +import { openPersonsModal } from '../persons-modal/PersonsModal' import { trendsDataLogic } from '../trendsDataLogic' -export function ActionsPie({ inSharedMode, inCardView, showPersonsModal = true }: ChartParams): JSX.Element | null { +export function ActionsPie({ + inSharedMode, + inCardView, + showPersonsModal = true, + context, +}: ChartParams): JSX.Element | null { const [data, setData] = useState(null) const [total, setTotal] = useState(0) @@ -29,10 +37,16 @@ export function ActionsPie({ inSharedMode, inCardView, showPersonsModal = true } trendsFilter, formula, showValueOnSeries, + showLabelOnSeries, supportsPercentStackView, showPercentStackView, + pieChartVizOptions, } = useValues(trendsDataLogic(insightProps)) + const renderingMetadata = context?.chartRenderingMetadata?.[ChartDisplayType.ActionsPie] + + const showAggregation = !pieChartVizOptions?.hideAggregation + function updateData(): void { const _data = [...indexedResults].sort((a, b) => b.aggregated_value - a.aggregated_value) const days = _data.length > 0 ? _data[0].days : [] @@ -69,6 +83,27 @@ export function ActionsPie({ inSharedMode, inCardView, showPersonsModal = true } } }, [indexedResults, hiddenLegendKeys]) + const onClick = + renderingMetadata?.onSegmentClick || + (!showPersonsModal || formula + ? undefined + : (payload) => { + const { points, index, crossDataset } = payload + const dataset = points.referencePoint.dataset + const label = dataset.labels?.[index] + + const urls = urlsForDatasets(crossDataset, index) + const selectedUrl = urls[index]?.value + + if (selectedUrl) { + openPersonsModal({ + urls, + urlsIndex: index, + title: , + }) + } + }) + return data ? ( data[0] && data[0].labels ? (
    @@ -86,33 +121,18 @@ export function ActionsPie({ inSharedMode, inCardView, showPersonsModal = true } trendsFilter={trendsFilter} formula={formula} showValueOnSeries={showValueOnSeries} + showLabelOnSeries={showLabelOnSeries} supportsPercentStackView={supportsPercentStackView} showPercentStackView={showPercentStackView} - onClick={ - !showPersonsModal || formula - ? undefined - : (payload) => { - const { points, index, crossDataset } = payload - const dataset = points.referencePoint.dataset - const label = dataset.labels?.[index] - - const urls = urlsForDatasets(crossDataset, index) - const selectedUrl = urls[index]?.value - - if (selectedUrl) { - openPersonsModal({ - urls, - urlsIndex: index, - title: , - }) - } - } - } + onClick={onClick} + disableHoverOffset={pieChartVizOptions?.disableHoverOffset} />
    -

    - {formatAggregationAxisValue(trendsFilter, total)} -

    + {showAggregation && ( +

    + {formatAggregationAxisValue(trendsFilter, total)} +

    + )}
    {inCardView && trendsFilter?.show_legend && }
    diff --git a/frontend/src/scenes/trends/viz/index.ts b/frontend/src/scenes/trends/viz/index.ts index 14965e37acde4..cd0636422f7c4 100644 --- a/frontend/src/scenes/trends/viz/index.ts +++ b/frontend/src/scenes/trends/viz/index.ts @@ -1,3 +1,3 @@ -export * from './ActionsPie' -export * from './ActionsLineGraph' export * from './ActionsHorizontalBar' +export * from './ActionsLineGraph' +export * from './ActionsPie' diff --git a/frontend/src/scenes/urls.ts b/frontend/src/scenes/urls.ts index 51a66af2ff593..9653e768dc3bc 100644 --- a/frontend/src/scenes/urls.ts +++ b/frontend/src/scenes/urls.ts @@ -1,19 +1,21 @@ +import { combineUrl } from 'kea-router' +import { toParams } from 'lib/utils' + +import { ExportOptions } from '~/exporter/types' import { ActionType, AnnotationType, AnyPartialFilterType, + AppMetricsUrlParams, DashboardType, FilterType, InsightShortId, - ReplayTabs, - PipelineTabs, - AppMetricsUrlParams, PipelineAppTabs, + PipelineTabs, + ReplayTabs, } from '~/types' -import { combineUrl } from 'kea-router' -import { ExportOptions } from '~/exporter/types' + import { PluginTab } from './plugins/types' -import { toParams } from 'lib/utils' import { SettingId, SettingLevelId, SettingSectionId } from './settings/types' /** diff --git a/frontend/src/scenes/userLogic.ts b/frontend/src/scenes/userLogic.ts index 251eace6e5cd5..d65757c264403 100644 --- a/frontend/src/scenes/userLogic.ts +++ b/frontend/src/scenes/userLogic.ts @@ -1,13 +1,15 @@ import { actions, afterMount, kea, listeners, path, reducers, selectors } from 'kea' -import api from 'lib/api' -import type { userLogicType } from './userLogicType' -import { AvailableFeature, OrganizationBasicType, ProductKey, UserType } from '~/types' -import posthog from 'posthog-js' -import { getAppContext } from 'lib/utils/getAppContext' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { loaders } from 'kea-loaders' import { forms } from 'kea-forms' +import { loaders } from 'kea-loaders' +import api from 'lib/api' import { DashboardCompatibleScenes } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { getAppContext } from 'lib/utils/getAppContext' +import posthog from 'posthog-js' + +import { AvailableFeature, OrganizationBasicType, ProductKey, UserType } from '~/types' + +import type { userLogicType } from './userLogicType' export interface UserDetailsFormType { first_name: string diff --git a/frontend/src/scenes/web-analytics/WebAnalyticsNotice.tsx b/frontend/src/scenes/web-analytics/WebAnalyticsNotice.tsx index a38e6b9164edf..62627d13eeac0 100644 --- a/frontend/src/scenes/web-analytics/WebAnalyticsNotice.tsx +++ b/frontend/src/scenes/web-analytics/WebAnalyticsNotice.tsx @@ -1,9 +1,9 @@ import { useActions, useValues } from 'kea' import { supportLogic } from 'lib/components/Support/supportLogic' -import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { IconBugReport, IconFeedback } from 'lib/lemon-ui/icons' import { LemonBanner } from 'lib/lemon-ui/LemonBanner' import { Link } from 'lib/lemon-ui/Link' -import { IconBugReport, IconFeedback } from 'lib/lemon-ui/icons' +import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' export const WebAnalyticsNotice = (): JSX.Element => { const { openSupportForm } = useActions(supportLogic) diff --git a/frontend/src/scenes/web-analytics/WebAnalyticsTile.tsx b/frontend/src/scenes/web-analytics/WebAnalyticsTile.tsx index fc20cfbf9d691..82465684496be 100644 --- a/frontend/src/scenes/web-analytics/WebAnalyticsTile.tsx +++ b/frontend/src/scenes/web-analytics/WebAnalyticsTile.tsx @@ -1,12 +1,13 @@ -import { QueryContext, QueryContextColumnComponent, QueryContextColumnTitleComponent } from '~/queries/types' -import { DataTableNode, InsightVizNode, NodeKind, WebStatsBreakdown } from '~/queries/schema' -import { UnexpectedNeverError } from 'lib/utils' import { useActions, useValues } from 'kea' -import { GeographyTab, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic' +import { UnexpectedNeverError } from 'lib/utils' import { useCallback, useMemo } from 'react' -import { Query } from '~/queries/Query/Query' import { countryCodeToFlag, countryCodeToName } from 'scenes/insights/views/WorldMap' -import { PropertyFilterType } from '~/types' +import { DeviceTab, GeographyTab, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic' + +import { Query } from '~/queries/Query/Query' +import { DataTableNode, InsightVizNode, NodeKind, WebStatsBreakdown } from '~/queries/schema' +import { QueryContext, QueryContextColumnComponent, QueryContextColumnTitleComponent } from '~/queries/types' +import { GraphPointPayload, PropertyFilterType } from '~/types' import { ChartDisplayType } from '~/types' const PercentageCell: QueryContextColumnComponent = ({ value }) => { @@ -173,9 +174,12 @@ export const webAnalyticsDataTableQueryContext: QueryContext = { } export const WebStatsTrendTile = ({ query }: { query: InsightVizNode }): JSX.Element => { - const { togglePropertyFilter, setGeographyTab } = useActions(webAnalyticsLogic) - const { hasCountryFilter } = useValues(webAnalyticsLogic) + const { togglePropertyFilter, setGeographyTab, setDeviceTab } = useActions(webAnalyticsLogic) + const { hasCountryFilter, deviceTab, hasDeviceTypeFilter, hasBrowserFilter, hasOSFilter } = + useValues(webAnalyticsLogic) const { key: worldMapPropertyName } = webStatsBreakdownToPropertyName(WebStatsBreakdown.Country) + const { key: deviceTypePropertyName } = webStatsBreakdownToPropertyName(WebStatsBreakdown.DeviceType) + const onWorldMapClick = useCallback( (breakdownValue: string) => { togglePropertyFilter(PropertyFilterType.Event, worldMapPropertyName, breakdownValue) @@ -187,6 +191,33 @@ export const WebStatsTrendTile = ({ query }: { query: InsightVizNode }): JSX.Ele [togglePropertyFilter, worldMapPropertyName] ) + const onDeviceTilePieChartClick = useCallback( + (graphPoint: GraphPointPayload) => { + if (graphPoint.seriesId == null) { + return + } + const dataset = graphPoint.crossDataset?.[graphPoint.seriesId] + if (!dataset) { + return + } + const breakdownValue = dataset.breakdownValues?.[graphPoint.index] + if (!breakdownValue) { + return + } + togglePropertyFilter(PropertyFilterType.Event, deviceTypePropertyName, breakdownValue) + + // switch to a different tab if we can, try them in this order: DeviceType Browser OS + if (deviceTab !== DeviceTab.DEVICE_TYPE && !hasDeviceTypeFilter) { + setDeviceTab(DeviceTab.DEVICE_TYPE) + } else if (deviceTab !== DeviceTab.BROWSER && !hasBrowserFilter) { + setDeviceTab(DeviceTab.BROWSER) + } else if (deviceTab !== DeviceTab.OS && !hasOSFilter) { + setDeviceTab(DeviceTab.OS) + } + }, + [togglePropertyFilter, deviceTypePropertyName, deviceTab, hasDeviceTypeFilter, hasBrowserFilter, hasOSFilter] + ) + const context = useMemo((): QueryContext => { return { ...webAnalyticsDataTableQueryContext, @@ -201,6 +232,9 @@ export const WebStatsTrendTile = ({ query }: { query: InsightVizNode }): JSX.Ele } }, }, + [ChartDisplayType.ActionsPie]: { + onSegmentClick: onDeviceTilePieChartClick, + }, }, } }, [onWorldMapClick]) diff --git a/frontend/src/scenes/web-analytics/WebDashboard.tsx b/frontend/src/scenes/web-analytics/WebDashboard.tsx index 1a01cd4051ab5..131832445ef55 100644 --- a/frontend/src/scenes/web-analytics/WebDashboard.tsx +++ b/frontend/src/scenes/web-analytics/WebDashboard.tsx @@ -1,10 +1,13 @@ -import { Query } from '~/queries/Query/Query' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { TabsTile, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic' +import { DateFilter } from 'lib/components/DateFilter/DateFilter' import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters' import { isEventPropertyOrPersonPropertyFilter } from 'lib/components/PropertyFilters/utils' -import { NodeKind, QuerySchema } from '~/queries/schema' -import { DateFilter } from 'lib/components/DateFilter/DateFilter' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { FEATURE_FLAGS } from 'lib/constants' +import { featureFlagLogic } from 'lib/logic/featureFlagLogic' +import { WebAnalyticsHealthCheck } from 'scenes/web-analytics/WebAnalyticsHealthCheck' +import { TabsTile, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic' import { WebAnalyticsNotice } from 'scenes/web-analytics/WebAnalyticsNotice' import { webAnalyticsDataTableQueryContext, @@ -12,11 +15,9 @@ import { WebStatsTrendTile, } from 'scenes/web-analytics/WebAnalyticsTile' import { WebTabs } from 'scenes/web-analytics/WebTabs' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' -import { featureFlagLogic } from 'lib/logic/featureFlagLogic' -import { FEATURE_FLAGS } from 'lib/constants' -import clsx from 'clsx' -import { WebAnalyticsHealthCheck } from 'scenes/web-analytics/WebAnalyticsHealthCheck' + +import { Query } from '~/queries/Query/Query' +import { NodeKind, QuerySchema } from '~/queries/schema' const Filters = (): JSX.Element => { const { webAnalyticsFilters, dateTo, dateFrom } = useValues(webAnalyticsLogic) @@ -152,7 +153,7 @@ const WebQuery = ({ query }: { query: QuerySchema }): JSX.Element => { export const WebAnalyticsDashboard = (): JSX.Element => { return ( -
    +
    diff --git a/frontend/src/scenes/web-analytics/WebTabs.tsx b/frontend/src/scenes/web-analytics/WebTabs.tsx index d96e72e9e5746..64cf389373d20 100644 --- a/frontend/src/scenes/web-analytics/WebTabs.tsx +++ b/frontend/src/scenes/web-analytics/WebTabs.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx' -import React from 'react' import { useSliderPositioning } from 'lib/lemon-ui/hooks' +import React from 'react' const TRANSITION_MS = 200 export const WebTabs = ({ @@ -59,7 +59,7 @@ export const WebTabs = ({
    -
    {activeTab?.content}
    +
    {activeTab?.content}
    ) } diff --git a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts index 84b83288da6cc..845629e3d7037 100644 --- a/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts +++ b/frontend/src/scenes/web-analytics/webAnalyticsLogic.ts @@ -1,6 +1,10 @@ import { actions, afterMount, connect, kea, path, reducers, selectors } from 'kea' - -import type { webAnalyticsLogicType } from './webAnalyticsLogicType' +import { loaders } from 'kea-loaders' +import { windowValues } from 'kea-window-values' +import api from 'lib/api' +import { RETENTION_FIRST_TIME, STALE_EVENT_SECONDS } from 'lib/constants' +import { dayjs } from 'lib/dayjs' +import { isNotNil } from 'lib/utils' import { NodeKind, @@ -20,12 +24,8 @@ import { PropertyOperator, RetentionPeriod, } from '~/types' -import { isNotNil } from 'lib/utils' -import { loaders } from 'kea-loaders' -import api from 'lib/api' -import { dayjs } from 'lib/dayjs' -import { RETENTION_FIRST_TIME, STALE_EVENT_SECONDS } from 'lib/constants' -import { windowValues } from 'kea-window-values' + +import type { webAnalyticsLogicType } from './webAnalyticsLogicType' export interface WebTileLayout { colSpan?: number @@ -184,7 +184,7 @@ export const webAnalyticsLogic = kea([ }, ], deviceTab: [ - DeviceTab.BROWSER as string, + DeviceTab.DEVICE_TYPE as string, { setDeviceTab: (_, { tab }) => tab, }, @@ -449,6 +449,39 @@ export const webAnalyticsLogic = kea([ activeTabId: deviceTab, setTabId: actions.setDeviceTab, tabs: [ + { + id: DeviceTab.DEVICE_TYPE, + title: 'Top Device Types', + linkText: 'Device Type', + query: { + kind: NodeKind.InsightVizNode, + source: { + kind: NodeKind.TrendsQuery, + breakdown: { breakdown: '$device_type', breakdown_type: 'event' }, + dateRange, + series: [ + { + event: '$pageview', + kind: NodeKind.EventsNode, + math: BaseMathType.UniqueUsers, + }, + ], + trendsFilter: { + display: ChartDisplayType.ActionsPie, + show_labels_on_series: true, + }, + filterTestAccounts: true, + properties: webAnalyticsFilters, + }, + hidePersonsModal: true, + vizSpecificOptions: { + [ChartDisplayType.ActionsPie]: { + disableHoverOffset: true, + hideAggregation: true, + }, + }, + }, + }, { id: DeviceTab.BROWSER, title: 'Top browsers', @@ -479,21 +512,6 @@ export const webAnalyticsLogic = kea([ }, }, }, - { - id: DeviceTab.DEVICE_TYPE, - title: 'Top device types', - linkText: 'Device type', - query: { - full: true, - kind: NodeKind.DataTableNode, - source: { - kind: NodeKind.WebStatsTableQuery, - properties: webAnalyticsFilters, - breakdownBy: WebStatsBreakdown.DeviceType, - dateRange, - }, - }, - }, ], }, { @@ -619,6 +637,24 @@ export const webAnalyticsLogic = kea([ return webAnalyticsFilters.some((filter) => filter.key === '$geoip_country_code') }, ], + hasDeviceTypeFilter: [ + (s) => [s.webAnalyticsFilters], + (webAnalyticsFilters: WebAnalyticsPropertyFilters) => { + return webAnalyticsFilters.some((filter) => filter.key === '$device_type') + }, + ], + hasBrowserFilter: [ + (s) => [s.webAnalyticsFilters], + (webAnalyticsFilters: WebAnalyticsPropertyFilters) => { + return webAnalyticsFilters.some((filter) => filter.key === '$browser') + }, + ], + hasOSFilter: [ + (s) => [s.webAnalyticsFilters], + (webAnalyticsFilters: WebAnalyticsPropertyFilters) => { + return webAnalyticsFilters.some((filter) => filter.key === '$os') + }, + ], })), loaders(() => ({ // load the status check query here and pass the response into the component, so the response diff --git a/frontend/src/styles/fonts.scss b/frontend/src/styles/fonts.scss index 9633ed07675ee..60443636e9b6b 100644 --- a/frontend/src/styles/fonts.scss +++ b/frontend/src/styles/fonts.scss @@ -29,3 +29,39 @@ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } + +/* Matter; bold (800); latin */ +@font-face { + font-family: MatterSQ; + font-style: normal; + font-weight: 800; + font-display: swap; + + // src: url('../../public/MatterSQ-Bold.woff2') format('woff2'), url('../../public/MatterSQ-Bold.woff') format('woff'); + src: url('https://d1sdjtjk6xzm7.cloudfront.net/MatterSQ-Bold.woff2') format('woff2'), + url('https://d1sdjtjk6xzm7.cloudfront.net/MatterSQ-Bold.woff') format('woff'); +} + +/* Matter; bold (700); latin */ +@font-face { + font-family: MatterSQ; + font-style: normal; + font-weight: 700; + font-display: swap; + + // src: url('../../public/MatterSQ-SemiBold.woff2') format('woff2'), url('../../public/MatterSQ-SemiBold.woff') format('woff'); + src: url('https://d1sdjtjk6xzm7.cloudfront.net/MatterSQ-SemiBold.woff2') format('woff2'), + url('https://d1sdjtjk6xzm7.cloudfront.net/MatterSQ-SemiBold.woff') format('woff'); +} + +/* Matter; medium (500); latin */ +@font-face { + font-family: MatterSQ; + font-style: normal; + font-weight: 500; + font-display: swap; + + // src: url('../../public/MatterSQ-Medium.woff2') format('woff2'), url('../../public/MatterSQ-Medium.woff') format('woff'); + src: url('https://d1sdjtjk6xzm7.cloudfront.net/public/MatterSQ-Medium.woff2') format('woff2'), + url('https://d1sdjtjk6xzm7.cloudfront.net/public/MatterSQ-Medium.woff') format('woff'); +} diff --git a/frontend/src/styles/global.scss b/frontend/src/styles/global.scss index 06991540e0ffa..69d3d66238b01 100644 --- a/frontend/src/styles/global.scss +++ b/frontend/src/styles/global.scss @@ -673,6 +673,13 @@ body { } } } + + h1, + h2, + h3, + h4 { + font-family: var(--font-title); + } } h1, diff --git a/frontend/src/styles/utilities.scss b/frontend/src/styles/utilities.scss index 0e33b9b17b2c1..d01caa82c1d77 100644 --- a/frontend/src/styles/utilities.scss +++ b/frontend/src/styles/utilities.scss @@ -1323,6 +1323,14 @@ $decorations: underline, overline, line-through, no-underline; visibility: hidden; } +.resize { + resize: both; +} + +.resize-x { + resize: horizontal; +} + .resize-y { resize: vertical; } diff --git a/frontend/src/styles/vars.scss b/frontend/src/styles/vars.scss index ed98ebc9baa7d..76bef66e65647 100644 --- a/frontend/src/styles/vars.scss +++ b/frontend/src/styles/vars.scss @@ -125,6 +125,7 @@ $colors: ( 'secondary-3000-hover-light': #cfd1c2, 'accent-3000-light': #eeefe9, 'bg-3000-light': #f3f4ef, + 'bg-hover-3000-light': #f3f4ef, 'border-3000-light': #dadbd2, 'border-bold-3000-light': #c1c2b9, 'glass-bg-3000-light': #e4e5deb3, @@ -157,11 +158,12 @@ $colors: ( 'secondary-3000-dark': #1d1f27, 'secondary-3000-hover-dark': #575d77, - 'accent-3000-dark': #232429, + 'accent-3000-dark': #21242b, 'bg-3000-dark': #1d1f27, - 'border-3000-dark': #2b2c32, + 'bg-hover-3000-dark': #292b36, + 'border-3000-dark': #35373e, 'border-bold-3000-dark': #3f4046, - 'glass-bg-3000-dark': #1d1f27b3, + 'glass-bg-3000-dark': #21242bb3, 'glass-border-3000-dark': var(--border-3000-dark), 'link-3000-dark': #f1a82c, @@ -189,10 +191,11 @@ $colors: ( 'secondary-3000-hover': var(--secondary-3000-hover), 'accent-3000': var(--accent-3000), 'bg-3000': var(--bg-3000), + 'bg-hover-3000': var(--bg-hover-3000), 'border-3000': var(--border-3000), 'border-bold-3000': var(--border-bold-3000), 'glass-bg-3000': var(--glass-bg-3000), - 'glass-border-3000': var(--glass-border-3000), + 'glass-border-3000': var(--border-3000), 'link-3000': var(--link-3000), // 'bg-light': var(--accent-3000), 'primary-3000-frame-bg': var(--primary-3000-frame-bg), @@ -238,8 +241,10 @@ $_lifecycle_dormant: $_danger; --opacity-disabled: 0.6; --font-medium: 500; --font-semibold: 600; - --font-sans: -apple-system, blinkmacsystemfont, 'Inter', 'Segoe UI', 'Roboto', 'Helvetica Neue', helvetica, arial, + --font-sans: -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Roboto', 'Helvetica Neue', helvetica, arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + --font-title: 'MatterSQ', -apple-system, BlinkMacSystemFont, 'Inter', 'Segoe UI', 'Roboto', 'Helvetica Neue', + helvetica, arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; --font-mono: ui-monospace, 'SFMono-Regular', 'SF Mono', 'Menlo', 'Consolas', 'Liberation Mono', monospace; // Dashboard item colors diff --git a/frontend/src/test/init.ts b/frontend/src/test/init.ts index 32e4e2b7d8110..13597c8eebb25 100644 --- a/frontend/src/test/init.ts +++ b/frontend/src/test/init.ts @@ -1,13 +1,14 @@ -import { initKea } from '~/initKea' -import { testUtilsPlugin } from 'kea-test-utils' import { createMemoryHistory } from 'history' -import posthog from 'posthog-js' -import { AppContext, TeamType } from '~/types' +import { testUtilsPlugin } from 'kea-test-utils' import { MOCK_DEFAULT_TEAM } from 'lib/api.mock' import { dayjs } from 'lib/dayjs' +import posthog from 'posthog-js' import { organizationLogic } from 'scenes/organizationLogic' -import { teamLogic } from 'scenes/teamLogic' import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic' +import { teamLogic } from 'scenes/teamLogic' + +import { initKea } from '~/initKea' +import { AppContext, TeamType } from '~/types' process.on('unhandledRejection', (err) => { console.warn(err) diff --git a/frontend/src/test/mocks.ts b/frontend/src/test/mocks.ts index c58aa57b47f29..dcd926d4f1e7a 100644 --- a/frontend/src/test/mocks.ts +++ b/frontend/src/test/mocks.ts @@ -1,3 +1,7 @@ +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' +import { PROPERTY_MATCH_TYPE } from 'lib/constants' +import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types' + import { BehavioralEventType, CohortType, @@ -13,9 +17,6 @@ import { TimeUnitType, UserBasicType, } from '~/types' -import { PROPERTY_MATCH_TYPE } from 'lib/constants' -import { BehavioralFilterKey } from 'scenes/cohorts/CohortFilters/types' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' export const mockBasicUser: UserBasicType = { id: 0, diff --git a/frontend/src/toolbar/Toolbar.stories.tsx b/frontend/src/toolbar/Toolbar.stories.tsx index 35ea9e6dd2e01..a0c98724f3230 100644 --- a/frontend/src/toolbar/Toolbar.stories.tsx +++ b/frontend/src/toolbar/Toolbar.stories.tsx @@ -1,12 +1,12 @@ import '~/styles' import '~/toolbar/styles.scss' -import { useEffect } from 'react' import { Meta } from '@storybook/react' +import { useEffect } from 'react' +import { useStorybookMocks } from '~/mocks/browser' import { ToolbarApp } from '~/toolbar/ToolbarApp' import { ToolbarParams } from '~/types' -import { useStorybookMocks } from '~/mocks/browser' const toolbarParams: ToolbarParams = { temporaryToken: 'UExb1dCsoqBtrhrZYxzmxXQ7XdjVH5Ea_zbQjTFuJqk', @@ -20,10 +20,10 @@ const toolbarParams: ToolbarParams = { const meta: Meta = { title: 'Scenes-Other/Toolbar', + tags: ['test-skip'], // This story is not valuable to snapshot as is parameters: { layout: 'fullscreen', viewMode: 'story', - testOptions: { skip: true }, // This story is not valuable to snapshot as is }, } export default meta diff --git a/frontend/src/toolbar/ToolbarApp.tsx b/frontend/src/toolbar/ToolbarApp.tsx index e50a73ac8301e..0b8e6c1790d79 100644 --- a/frontend/src/toolbar/ToolbarApp.tsx +++ b/frontend/src/toolbar/ToolbarApp.tsx @@ -1,11 +1,12 @@ -import { useRef, useState } from 'react' +import { useValues } from 'kea' import { useSecondRender } from 'lib/hooks/useSecondRender' +import { useRef, useState } from 'react' import root from 'react-shadow' +import { Slide, ToastContainer } from 'react-toastify' + import { ToolbarContainer } from '~/toolbar/ToolbarContainer' -import { useValues } from 'kea' import { toolbarLogic } from '~/toolbar/toolbarLogic' import { ToolbarProps } from '~/types' -import { Slide, ToastContainer } from 'react-toastify' type HTMLElementWithShadowRoot = HTMLElement & { shadowRoot: ShadowRoot } diff --git a/frontend/src/toolbar/ToolbarContainer.tsx b/frontend/src/toolbar/ToolbarContainer.tsx index b184ccfdde469..e6aebff4342e9 100644 --- a/frontend/src/toolbar/ToolbarContainer.tsx +++ b/frontend/src/toolbar/ToolbarContainer.tsx @@ -1,8 +1,9 @@ import { useValues } from 'kea' -import { Elements } from '~/toolbar/elements/Elements' +import { Fade } from 'lib/components/Fade/Fade' + import { DraggableButton } from '~/toolbar/button/DraggableButton' +import { Elements } from '~/toolbar/elements/Elements' import { toolbarLogic } from '~/toolbar/toolbarLogic' -import { Fade } from 'lib/components/Fade/Fade' export function ToolbarContainer(): JSX.Element { const { buttonVisible } = useValues(toolbarLogic) diff --git a/frontend/src/toolbar/actions/ActionsList.tsx b/frontend/src/toolbar/actions/ActionsList.tsx index ab6ef76c72dd0..d8cdfb7c3ffb8 100644 --- a/frontend/src/toolbar/actions/ActionsList.tsx +++ b/frontend/src/toolbar/actions/ActionsList.tsx @@ -1,11 +1,12 @@ import { useActions, useValues } from 'kea' +import { IconPlus } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' + +import { ActionsListView } from '~/toolbar/actions/ActionsListView' import { actionsLogic } from '~/toolbar/actions/actionsLogic' import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' -import { ActionsListView } from '~/toolbar/actions/ActionsListView' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconPlus } from 'lib/lemon-ui/icons' export function ActionsList(): JSX.Element { const { allActions, sortedActions, allActionsLoading, searchTerm } = useValues(actionsLogic) diff --git a/frontend/src/toolbar/actions/ActionsListView.tsx b/frontend/src/toolbar/actions/ActionsListView.tsx index 7f2a25c0db10f..2293a594c5d31 100644 --- a/frontend/src/toolbar/actions/ActionsListView.tsx +++ b/frontend/src/toolbar/actions/ActionsListView.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' +import { Spinner } from 'lib/lemon-ui/Spinner' + +import { actionsLogic } from '~/toolbar/actions/actionsLogic' import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { ActionType } from '~/types' -import { actionsLogic } from '~/toolbar/actions/actionsLogic' -import { Spinner } from 'lib/lemon-ui/Spinner' interface ActionsListViewProps { actions: ActionType[] diff --git a/frontend/src/toolbar/actions/ActionsTab.tsx b/frontend/src/toolbar/actions/ActionsTab.tsx index 981eb79a15877..71d06c73c87de 100644 --- a/frontend/src/toolbar/actions/ActionsTab.tsx +++ b/frontend/src/toolbar/actions/ActionsTab.tsx @@ -1,12 +1,13 @@ import './ActionsTab.scss' +import { Link } from '@posthog/lemon-ui' import { useValues } from 'kea' -import { toolbarLogic } from '~/toolbar/toolbarLogic' +import { urls } from 'scenes/urls' + import { ActionsList } from '~/toolbar/actions/ActionsList' import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { EditAction } from '~/toolbar/actions/EditAction' -import { urls } from 'scenes/urls' -import { Link } from '@posthog/lemon-ui' +import { toolbarLogic } from '~/toolbar/toolbarLogic' export function ActionsTab(): JSX.Element { const { selectedAction } = useValues(actionsTabLogic) diff --git a/frontend/src/toolbar/actions/ButtonWindow.stories.tsx b/frontend/src/toolbar/actions/ButtonWindow.stories.tsx index 3b98177f17f05..b31e1a3e9f13c 100644 --- a/frontend/src/toolbar/actions/ButtonWindow.stories.tsx +++ b/frontend/src/toolbar/actions/ButtonWindow.stories.tsx @@ -1,14 +1,15 @@ import { Meta, StoryFn, StoryObj } from '@storybook/react' import { useMountedLogic } from 'kea' + import { useStorybookMocks } from '~/mocks/browser' -import { useToolbarStyles } from '~/toolbar/Toolbar.stories' -import { toolbarLogic } from '~/toolbar/toolbarLogic' -import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' +import { actionsLogic } from '~/toolbar/actions/actionsLogic' import { ActionsTab } from '~/toolbar/actions/ActionsTab' +import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { ButtonWindow } from '~/toolbar/button/ButtonWindow' import { FeatureFlags } from '~/toolbar/flags/FeatureFlags' import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic' -import { actionsLogic } from '~/toolbar/actions/actionsLogic' +import { useToolbarStyles } from '~/toolbar/Toolbar.stories' +import { toolbarLogic } from '~/toolbar/toolbarLogic' interface StoryProps { contents: JSX.Element name: string diff --git a/frontend/src/toolbar/actions/EditAction.tsx b/frontend/src/toolbar/actions/EditAction.tsx index 409f439565704..782c2c10b42c7 100644 --- a/frontend/src/toolbar/actions/EditAction.tsx +++ b/frontend/src/toolbar/actions/EditAction.tsx @@ -1,13 +1,14 @@ import { useActions, useValues } from 'kea' +import { Field, Form, Group } from 'kea-forms' +import { IconClose, IconDelete, IconEdit, IconMagnifier, IconMinusOutlined, IconPlus } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' + import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { StepField } from '~/toolbar/actions/StepField' import { SelectorEditingModal } from '~/toolbar/elements/SelectorEditingModal' -import { LemonButton } from 'lib/lemon-ui/LemonButton' -import { IconClose, IconDelete, IconEdit, IconMagnifier, IconMinusOutlined, IconPlus } from 'lib/lemon-ui/icons' import { posthog } from '~/toolbar/posthog' import { getShadowRootPopoverContainer } from '~/toolbar/utils' -import { Field, Form, Group } from 'kea-forms' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' export function EditAction(): JSX.Element { const { diff --git a/frontend/src/toolbar/actions/SelectorCount.tsx b/frontend/src/toolbar/actions/SelectorCount.tsx index 20a2b764d894f..205620c655d5c 100644 --- a/frontend/src/toolbar/actions/SelectorCount.tsx +++ b/frontend/src/toolbar/actions/SelectorCount.tsx @@ -1,5 +1,5 @@ -import { useMemo } from 'react' import { querySelectorAllDeep } from 'query-selector-shadow-dom' +import { useMemo } from 'react' interface SelectorCountProps { selector: string diff --git a/frontend/src/toolbar/actions/StepField.tsx b/frontend/src/toolbar/actions/StepField.tsx index b066bae78d239..42729674f99c9 100644 --- a/frontend/src/toolbar/actions/StepField.tsx +++ b/frontend/src/toolbar/actions/StepField.tsx @@ -1,14 +1,15 @@ -import { SelectorCount } from '~/toolbar/actions/SelectorCount' -import { cssEscape } from 'lib/utils/cssEscape' -import { ActionStepForm } from '~/toolbar/types' -import { URL_MATCHING_HINTS } from 'scenes/actions/hints' +import clsx from 'clsx' import { Field } from 'kea-forms' import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { StringMatching } from '~/types' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonSegmentedButton } from 'lib/lemon-ui/LemonSegmentedButton' import { LemonTextArea } from 'lib/lemon-ui/LemonTextArea/LemonTextArea' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import clsx from 'clsx' +import { cssEscape } from 'lib/utils/cssEscape' +import { URL_MATCHING_HINTS } from 'scenes/actions/hints' + +import { SelectorCount } from '~/toolbar/actions/SelectorCount' +import { ActionStepForm } from '~/toolbar/types' +import { StringMatching } from '~/types' interface StepFieldProps { item: 'href' | 'text' | 'selector' | 'url' diff --git a/frontend/src/toolbar/actions/actionsLogic.test.ts b/frontend/src/toolbar/actions/actionsLogic.test.ts index a832eaf32e536..7c52916927a73 100644 --- a/frontend/src/toolbar/actions/actionsLogic.test.ts +++ b/frontend/src/toolbar/actions/actionsLogic.test.ts @@ -1,4 +1,5 @@ import { expectLogic } from 'kea-test-utils' + import { initKeaTests } from '~/test/init' import { actionsLogic } from '~/toolbar/actions/actionsLogic' import { toolbarLogic } from '~/toolbar/toolbarLogic' diff --git a/frontend/src/toolbar/actions/actionsLogic.ts b/frontend/src/toolbar/actions/actionsLogic.ts index 8d02e177c0f94..68c101a7fb532 100644 --- a/frontend/src/toolbar/actions/actionsLogic.ts +++ b/frontend/src/toolbar/actions/actionsLogic.ts @@ -1,10 +1,12 @@ +import Fuse from 'fuse.js' +import { actions, kea, path, reducers, selectors } from 'kea' import { loaders } from 'kea-loaders' -import { kea, path, actions, reducers, selectors } from 'kea' + import { toolbarLogic } from '~/toolbar/toolbarLogic' -import type { actionsLogicType } from './actionsLogicType' -import { ActionType } from '~/types' -import Fuse from 'fuse.js' import { toolbarFetch } from '~/toolbar/utils' +import { ActionType } from '~/types' + +import type { actionsLogicType } from './actionsLogicType' export const actionsLogic = kea([ path(['toolbar', 'actions', 'actionsLogic']), diff --git a/frontend/src/toolbar/actions/actionsTabLogic.tsx b/frontend/src/toolbar/actions/actionsTabLogic.tsx index c19cecf532c63..0a0ab5235c938 100644 --- a/frontend/src/toolbar/actions/actionsTabLogic.tsx +++ b/frontend/src/toolbar/actions/actionsTabLogic.tsx @@ -1,17 +1,19 @@ -import { kea, path, actions, connect, reducers, selectors, listeners } from 'kea' +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { forms } from 'kea-forms' +import { subscriptions } from 'kea-subscriptions' import api from 'lib/api' +import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { urls } from 'scenes/urls' + import { actionsLogic } from '~/toolbar/actions/actionsLogic' -import { actionStepToActionStepFormItem, elementToActionStep, stepToDatabaseFormat } from '~/toolbar/utils' -import { toolbarLogic } from '~/toolbar/toolbarLogic' import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' -import type { actionsTabLogicType } from './actionsTabLogicType' -import { ActionType, ElementType } from '~/types' -import { ActionDraftType, ActionForm } from '~/toolbar/types' import { posthog } from '~/toolbar/posthog' -import { lemonToast } from 'lib/lemon-ui/lemonToast' -import { urls } from 'scenes/urls' -import { forms } from 'kea-forms' -import { subscriptions } from 'kea-subscriptions' +import { toolbarLogic } from '~/toolbar/toolbarLogic' +import { ActionDraftType, ActionForm } from '~/toolbar/types' +import { actionStepToActionStepFormItem, elementToActionStep, stepToDatabaseFormat } from '~/toolbar/utils' +import { ActionType, ElementType } from '~/types' + +import type { actionsTabLogicType } from './actionsTabLogicType' function newAction(element: HTMLElement | null, dataAttributes: string[] = []): ActionDraftType { return { diff --git a/frontend/src/toolbar/button/ButtonWindow.tsx b/frontend/src/toolbar/button/ButtonWindow.tsx index 5e93376c75f47..bba87b81030e8 100644 --- a/frontend/src/toolbar/button/ButtonWindow.tsx +++ b/frontend/src/toolbar/button/ButtonWindow.tsx @@ -1,7 +1,7 @@ +import { LemonButton } from '@posthog/lemon-ui' import { Fade } from 'lib/components/Fade/Fade' -import Draggable from 'react-draggable' import { IconClose } from 'lib/lemon-ui/icons' -import { LemonButton } from '@posthog/lemon-ui' +import Draggable from 'react-draggable' interface ButtonWindowProps { name: string diff --git a/frontend/src/toolbar/button/DraggableButton.tsx b/frontend/src/toolbar/button/DraggableButton.tsx index 392dc31b27b44..08f66d767ed71 100644 --- a/frontend/src/toolbar/button/DraggableButton.tsx +++ b/frontend/src/toolbar/button/DraggableButton.tsx @@ -1,13 +1,15 @@ -import { ToolbarButton } from '~/toolbar/button/ToolbarButton' -import Draggable from 'react-draggable' -import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' import { useActions, useValues } from 'kea' -import { HeatmapStats } from '~/toolbar/stats/HeatmapStats' +import Draggable from 'react-draggable' + import { ActionsTab } from '~/toolbar/actions/ActionsTab' import { ButtonWindow } from '~/toolbar/button/ButtonWindow' -import { posthog } from '~/toolbar/posthog' +import { ToolbarButton } from '~/toolbar/button/ToolbarButton' +import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' import { FeatureFlags } from '~/toolbar/flags/FeatureFlags' import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic' +import { posthog } from '~/toolbar/posthog' +import { HeatmapStats } from '~/toolbar/stats/HeatmapStats' + import { HedgehogButton } from './HedgehogButton' export function DraggableButton(): JSX.Element { diff --git a/frontend/src/toolbar/button/HedgehogButton.tsx b/frontend/src/toolbar/button/HedgehogButton.tsx index 44f1b20ae2d0b..026b6d6c30717 100644 --- a/frontend/src/toolbar/button/HedgehogButton.tsx +++ b/frontend/src/toolbar/button/HedgehogButton.tsx @@ -1,10 +1,12 @@ -import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' import { useActions, useValues } from 'kea' import { HedgehogActor, HedgehogBuddy } from 'lib/components/HedgehogBuddy/HedgehogBuddy' import { SPRITE_SIZE } from 'lib/components/HedgehogBuddy/sprites/sprites' -import { toolbarLogic } from '../toolbarLogic' import { useEffect, useRef } from 'react' + +import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' + import { heatmapLogic } from '../elements/heatmapLogic' +import { toolbarLogic } from '../toolbarLogic' export function HedgehogButton(): JSX.Element { const { hedgehogMode, extensionPercentage } = useValues(toolbarButtonLogic) diff --git a/frontend/src/toolbar/button/ToolbarButton.tsx b/frontend/src/toolbar/button/ToolbarButton.tsx index b4d3e9bc38a6d..564a6689e6224 100644 --- a/frontend/src/toolbar/button/ToolbarButton.tsx +++ b/frontend/src/toolbar/button/ToolbarButton.tsx @@ -1,23 +1,24 @@ import './ToolbarButton.scss' -import { useRef, useEffect } from 'react' import { useActions, useValues } from 'kea' +import { useLongPress } from 'lib/hooks/useLongPress' +import { IconHelpOutline, IconTarget } from 'lib/lemon-ui/icons' +import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { useEffect, useRef } from 'react' + +import { actionsLogic } from '~/toolbar/actions/actionsLogic' +import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { Logomark } from '~/toolbar/assets/Logomark' import { Circle } from '~/toolbar/button/Circle' +import { Close } from '~/toolbar/button/icons/Close' +import { Fire } from '~/toolbar/button/icons/Fire' +import { Flag } from '~/toolbar/button/icons/Flag' +import { Magnifier } from '~/toolbar/button/icons/Magnifier' import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' +import { elementsLogic } from '~/toolbar/elements/elementsLogic' import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' import { toolbarLogic } from '~/toolbar/toolbarLogic' import { getShadowRoot, getShadowRootPopoverContainer } from '~/toolbar/utils' -import { elementsLogic } from '~/toolbar/elements/elementsLogic' -import { useLongPress } from 'lib/hooks/useLongPress' -import { Flag } from '~/toolbar/button/icons/Flag' -import { Fire } from '~/toolbar/button/icons/Fire' -import { Magnifier } from '~/toolbar/button/icons/Magnifier' -import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' -import { actionsLogic } from '~/toolbar/actions/actionsLogic' -import { Close } from '~/toolbar/button/icons/Close' -import { Tooltip } from 'lib/lemon-ui/Tooltip' -import { IconHelpOutline, IconTarget } from 'lib/lemon-ui/icons' const HELP_URL = 'https://posthog.com/docs/user-guides/toolbar?utm_medium=in-product&utm_campaign=toolbar-help-button' diff --git a/frontend/src/toolbar/button/toolbarButtonLogic.ts b/frontend/src/toolbar/button/toolbarButtonLogic.ts index f8d34f07ba4aa..68a0f3b7609d6 100644 --- a/frontend/src/toolbar/button/toolbarButtonLogic.ts +++ b/frontend/src/toolbar/button/toolbarButtonLogic.ts @@ -1,12 +1,14 @@ +import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' import { windowValues } from 'kea-window-values' -import { kea, path, connect, actions, reducers, selectors, listeners } from 'kea' -import { inBounds } from '~/toolbar/utils' -import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' -import { elementsLogic } from '~/toolbar/elements/elementsLogic' +import { HedgehogActor } from 'lib/components/HedgehogBuddy/HedgehogBuddy' + import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' -import type { toolbarButtonLogicType } from './toolbarButtonLogicType' +import { elementsLogic } from '~/toolbar/elements/elementsLogic' +import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' import { posthog } from '~/toolbar/posthog' -import { HedgehogActor } from 'lib/components/HedgehogBuddy/HedgehogBuddy' +import { inBounds } from '~/toolbar/utils' + +import type { toolbarButtonLogicType } from './toolbarButtonLogicType' export const toolbarButtonLogic = kea([ path(['toolbar', 'button', 'toolbarButtonLogic']), diff --git a/frontend/src/toolbar/elements/ElementInfo.tsx b/frontend/src/toolbar/elements/ElementInfo.tsx index ffa34a43788d0..1c8e59b7b5809 100644 --- a/frontend/src/toolbar/elements/ElementInfo.tsx +++ b/frontend/src/toolbar/elements/ElementInfo.tsx @@ -1,10 +1,11 @@ +import { LemonButton } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { IconCalendar, IconPlus } from 'lib/lemon-ui/icons' + +import { ActionsListView } from '~/toolbar/actions/ActionsListView' import { ActionStep } from '~/toolbar/elements/ActionStep' -import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' import { elementsLogic } from '~/toolbar/elements/elementsLogic' -import { ActionsListView } from '~/toolbar/actions/ActionsListView' -import { LemonButton } from '@posthog/lemon-ui' -import { IconCalendar, IconPlus } from 'lib/lemon-ui/icons' +import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' function ElementStatistic({ prefix, diff --git a/frontend/src/toolbar/elements/Elements.tsx b/frontend/src/toolbar/elements/Elements.tsx index b65605cad3706..f78e50445ecbd 100644 --- a/frontend/src/toolbar/elements/Elements.tsx +++ b/frontend/src/toolbar/elements/Elements.tsx @@ -1,15 +1,16 @@ import './Elements.scss' -import React from 'react' import { useActions, useValues } from 'kea' -import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' +import { compactNumber } from 'lib/utils' +import React from 'react' + +import { elementsLogic } from '~/toolbar/elements/elementsLogic' import { FocusRect } from '~/toolbar/elements/FocusRect' -import { InfoWindow } from '~/toolbar/elements/InfoWindow' import { HeatmapElement } from '~/toolbar/elements/HeatmapElement' import { HeatmapLabel } from '~/toolbar/elements/HeatmapLabel' -import { elementsLogic } from '~/toolbar/elements/elementsLogic' +import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' +import { InfoWindow } from '~/toolbar/elements/InfoWindow' import { getBoxColors, getHeatMapHue } from '~/toolbar/utils' -import { compactNumber } from 'lib/utils' export function Elements(): JSX.Element { const { diff --git a/frontend/src/toolbar/elements/HeatmapLabel.tsx b/frontend/src/toolbar/elements/HeatmapLabel.tsx index c8bac3793a2d1..5b1a77d301afb 100644 --- a/frontend/src/toolbar/elements/HeatmapLabel.tsx +++ b/frontend/src/toolbar/elements/HeatmapLabel.tsx @@ -1,5 +1,5 @@ -import { inBounds } from '~/toolbar/utils' import { ElementRect } from '~/toolbar/types' +import { inBounds } from '~/toolbar/utils' const heatmapLabelStyle = { lineHeight: '14px', diff --git a/frontend/src/toolbar/elements/InfoWindow.tsx b/frontend/src/toolbar/elements/InfoWindow.tsx index 7a5c39515e87a..a8ba8876e36f8 100644 --- a/frontend/src/toolbar/elements/InfoWindow.tsx +++ b/frontend/src/toolbar/elements/InfoWindow.tsx @@ -1,8 +1,9 @@ import { useActions, useValues } from 'kea' -import { elementsLogic } from '~/toolbar/elements/elementsLogic' -import { ElementInfo } from '~/toolbar/elements/ElementInfo' import { IconClose } from 'lib/lemon-ui/icons' +import { ElementInfo } from '~/toolbar/elements/ElementInfo' +import { elementsLogic } from '~/toolbar/elements/elementsLogic' + export function InfoWindow(): JSX.Element | null { const { hoverElement, diff --git a/frontend/src/toolbar/elements/SelectorEditingModal.tsx b/frontend/src/toolbar/elements/SelectorEditingModal.tsx index ee574eed6b7ac..3f3966915cfa5 100644 --- a/frontend/src/toolbar/elements/SelectorEditingModal.tsx +++ b/frontend/src/toolbar/elements/SelectorEditingModal.tsx @@ -1,8 +1,9 @@ -import { getShadowRootPopoverContainer } from '~/toolbar/utils' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { HTMLElementsDisplay } from 'lib/components/HTMLElementsDisplay/HTMLElementsDisplay' +import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonModal } from 'lib/lemon-ui/LemonModal' import { useState } from 'react' + +import { getShadowRootPopoverContainer } from '~/toolbar/utils' import { ElementType } from '~/types' export const SelectorEditingModal = ({ diff --git a/frontend/src/toolbar/elements/elementsLogic.ts b/frontend/src/toolbar/elements/elementsLogic.ts index ede7fd0a99ae2..9aaa7a665a8b7 100644 --- a/frontend/src/toolbar/elements/elementsLogic.ts +++ b/frontend/src/toolbar/elements/elementsLogic.ts @@ -1,16 +1,17 @@ -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' +import { collectAllElementsDeep } from 'query-selector-shadow-dom' import { actionsLogic } from '~/toolbar/actions/actionsLogic' -import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' -import { elementToActionStep, getAllClickTargets, getElementForStep, getRectForElement } from '~/toolbar/utils' import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' -import type { elementsLogicType } from './elementsLogicType' -import { ActionElementWithMetadata, ElementWithMetadata } from '~/toolbar/types' +import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' +import { posthog } from '~/toolbar/posthog' import { currentPageLogic } from '~/toolbar/stats/currentPageLogic' import { toolbarLogic } from '~/toolbar/toolbarLogic' -import { posthog } from '~/toolbar/posthog' -import { collectAllElementsDeep } from 'query-selector-shadow-dom' +import { ActionElementWithMetadata, ElementWithMetadata } from '~/toolbar/types' +import { elementToActionStep, getAllClickTargets, getElementForStep, getRectForElement } from '~/toolbar/utils' + +import type { elementsLogicType } from './elementsLogicType' export type ActionElementMap = Map export type ElementMap = Map diff --git a/frontend/src/toolbar/elements/heatmapLogic.ts b/frontend/src/toolbar/elements/heatmapLogic.ts index 773201702befc..516717c8b701d 100644 --- a/frontend/src/toolbar/elements/heatmapLogic.ts +++ b/frontend/src/toolbar/elements/heatmapLogic.ts @@ -1,17 +1,19 @@ import { actions, afterMount, beforeUnmount, connect, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' import { encodeParams } from 'kea-router' +import { elementToSelector, escapeRegex } from 'lib/actionUtils' +import { PaginatedResponse } from 'lib/api' +import { dateFilterToText } from 'lib/utils' +import { collectAllElementsDeep, querySelectorAllDeep } from 'query-selector-shadow-dom' + +import { posthog } from '~/toolbar/posthog' import { currentPageLogic } from '~/toolbar/stats/currentPageLogic' -import { elementToActionStep, toolbarFetch, trimElement } from '~/toolbar/utils' import { toolbarLogic } from '~/toolbar/toolbarLogic' -import type { heatmapLogicType } from './heatmapLogicType' import { CountedHTMLElement, ElementsEventType } from '~/toolbar/types' -import { posthog } from '~/toolbar/posthog' -import { collectAllElementsDeep, querySelectorAllDeep } from 'query-selector-shadow-dom' -import { elementToSelector, escapeRegex } from 'lib/actionUtils' +import { elementToActionStep, toolbarFetch, trimElement } from '~/toolbar/utils' import { FilterType, PropertyFilterType, PropertyOperator } from '~/types' -import { PaginatedResponse } from 'lib/api' -import { loaders } from 'kea-loaders' -import { dateFilterToText } from 'lib/utils' + +import type { heatmapLogicType } from './heatmapLogicType' const emptyElementsStatsPages: PaginatedResponse = { next: undefined, @@ -21,9 +23,9 @@ const emptyElementsStatsPages: PaginatedResponse = { export const heatmapLogic = kea([ path(['toolbar', 'elements', 'heatmapLogic']), - connect({ + connect(() => ({ values: [toolbarLogic, ['apiURL']], - }), + })), actions({ getElementStats: (url?: string | null) => ({ url, diff --git a/frontend/src/toolbar/flags/FeatureFlags.tsx b/frontend/src/toolbar/flags/FeatureFlags.tsx index 05698a0df7082..2da4f27b918a7 100644 --- a/frontend/src/toolbar/flags/FeatureFlags.tsx +++ b/frontend/src/toolbar/flags/FeatureFlags.tsx @@ -1,16 +1,17 @@ import './featureFlags.scss' +import { Link } from '@posthog/lemon-ui' +import clsx from 'clsx' import { useActions, useValues } from 'kea' -import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic' import { AnimatedCollapsible } from 'lib/components/AnimatedCollapsible' -import { toolbarLogic } from '~/toolbar/toolbarLogic' -import { urls } from 'scenes/urls' +import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import clsx from 'clsx' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' import { Spinner } from 'lib/lemon-ui/Spinner' -import { LemonCheckbox } from 'lib/lemon-ui/LemonCheckbox' -import { Link } from '@posthog/lemon-ui' +import { urls } from 'scenes/urls' + +import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic' +import { toolbarLogic } from '~/toolbar/toolbarLogic' export function FeatureFlags(): JSX.Element { const { searchTerm, filteredFlags, userFlagsLoading } = useValues(featureFlagsLogic) diff --git a/frontend/src/toolbar/flags/featureFlagsLogic.test.ts b/frontend/src/toolbar/flags/featureFlagsLogic.test.ts index 42bd952951a0c..f0f04d87060dd 100644 --- a/frontend/src/toolbar/flags/featureFlagsLogic.test.ts +++ b/frontend/src/toolbar/flags/featureFlagsLogic.test.ts @@ -1,4 +1,5 @@ import { expectLogic } from 'kea-test-utils' + import { initKeaTests } from '~/test/init' import { featureFlagsLogic } from '~/toolbar/flags/featureFlagsLogic' import { toolbarLogic } from '~/toolbar/toolbarLogic' diff --git a/frontend/src/toolbar/flags/featureFlagsLogic.ts b/frontend/src/toolbar/flags/featureFlagsLogic.ts index 67485f6d708ce..a6e21b5458aab 100644 --- a/frontend/src/toolbar/flags/featureFlagsLogic.ts +++ b/frontend/src/toolbar/flags/featureFlagsLogic.ts @@ -1,13 +1,15 @@ -import { loaders } from 'kea-loaders' -import { kea, path, connect, actions, reducers, selectors, listeners, events } from 'kea' -import { CombinedFeatureFlagAndValueType } from '~/types' -import type { featureFlagsLogicType } from './featureFlagsLogicType' -import { toolbarFetch } from '~/toolbar/utils' -import { toolbarLogic } from '~/toolbar/toolbarLogic' import Fuse from 'fuse.js' +import { actions, connect, events, kea, listeners, path, reducers, selectors } from 'kea' +import { loaders } from 'kea-loaders' +import { encodeParams } from 'kea-router' import type { PostHog } from 'posthog-js' + import { posthog } from '~/toolbar/posthog' -import { encodeParams } from 'kea-router' +import { toolbarLogic } from '~/toolbar/toolbarLogic' +import { toolbarFetch } from '~/toolbar/utils' +import { CombinedFeatureFlagAndValueType } from '~/types' + +import type { featureFlagsLogicType } from './featureFlagsLogicType' export const featureFlagsLogic = kea([ path(['toolbar', 'flags', 'featureFlagsLogic']), diff --git a/frontend/src/toolbar/index.tsx b/frontend/src/toolbar/index.tsx index b88fb22c6f88e..66f36bd4f45ad 100644 --- a/frontend/src/toolbar/index.tsx +++ b/frontend/src/toolbar/index.tsx @@ -1,11 +1,12 @@ import '~/styles' import './styles.scss' +import { PostHog } from 'posthog-js' import { createRoot } from 'react-dom/client' + import { initKea } from '~/initKea' import { ToolbarApp } from '~/toolbar/ToolbarApp' import { ToolbarParams } from '~/types' -import { PostHog } from 'posthog-js' ;(window as any)['ph_load_toolbar'] = function (toolbarParams: ToolbarParams, posthog: PostHog) { initKea() const container = document.createElement('div') diff --git a/frontend/src/toolbar/stats/HeatmapStats.tsx b/frontend/src/toolbar/stats/HeatmapStats.tsx index ab557cf416774..556c0850ecd62 100644 --- a/frontend/src/toolbar/stats/HeatmapStats.tsx +++ b/frontend/src/toolbar/stats/HeatmapStats.tsx @@ -1,16 +1,17 @@ import { useActions, useValues } from 'kea' -import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' -import { elementsLogic } from '~/toolbar/elements/elementsLogic' -import { getShadowRootPopoverContainer } from '~/toolbar/utils' import { DateFilter } from 'lib/components/DateFilter/DateFilter' -import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' -import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' -import { currentPageLogic } from '~/toolbar/stats/currentPageLogic' -import { LemonButton } from 'lib/lemon-ui/LemonButton' import { IconSync } from 'lib/lemon-ui/icons' +import { LemonButton } from 'lib/lemon-ui/LemonButton' +import { LemonInput } from 'lib/lemon-ui/LemonInput/LemonInput' import { LemonSwitch } from 'lib/lemon-ui/LemonSwitch/LemonSwitch' +import { Spinner } from 'lib/lemon-ui/Spinner/Spinner' import { Tooltip } from 'lib/lemon-ui/Tooltip' +import { elementsLogic } from '~/toolbar/elements/elementsLogic' +import { heatmapLogic } from '~/toolbar/elements/heatmapLogic' +import { currentPageLogic } from '~/toolbar/stats/currentPageLogic' +import { getShadowRootPopoverContainer } from '~/toolbar/utils' + export function HeatmapStats(): JSX.Element { const { matchLinksByHref, diff --git a/frontend/src/toolbar/stats/currentPageLogic.ts b/frontend/src/toolbar/stats/currentPageLogic.ts index 97f372f96f899..d637a6a488af3 100644 --- a/frontend/src/toolbar/stats/currentPageLogic.ts +++ b/frontend/src/toolbar/stats/currentPageLogic.ts @@ -1,4 +1,5 @@ -import { kea, path, actions, reducers, events } from 'kea' +import { actions, events, kea, path, reducers } from 'kea' + import type { currentPageLogicType } from './currentPageLogicType' export const currentPageLogic = kea([ diff --git a/frontend/src/toolbar/toolbarLogic.test.ts b/frontend/src/toolbar/toolbarLogic.test.ts index 49e36cd19127a..0d4354c3441f6 100644 --- a/frontend/src/toolbar/toolbarLogic.test.ts +++ b/frontend/src/toolbar/toolbarLogic.test.ts @@ -1,7 +1,8 @@ -import { toolbarLogic } from '~/toolbar/toolbarLogic' -import { initKeaTests } from '~/test/init' import { expectLogic } from 'kea-test-utils' +import { initKeaTests } from '~/test/init' +import { toolbarLogic } from '~/toolbar/toolbarLogic' + global.fetch = jest.fn(() => Promise.resolve({ ok: true, diff --git a/frontend/src/toolbar/toolbarLogic.ts b/frontend/src/toolbar/toolbarLogic.ts index c4759bb29d511..1394e1d1d05f2 100644 --- a/frontend/src/toolbar/toolbarLogic.ts +++ b/frontend/src/toolbar/toolbarLogic.ts @@ -1,11 +1,13 @@ import { actions, afterMount, kea, listeners, path, props, reducers, selectors } from 'kea' -import type { toolbarLogicType } from './toolbarLogicType' -import { ToolbarProps } from '~/types' -import { clearSessionToolbarToken } from '~/toolbar/utils' -import { posthog } from '~/toolbar/posthog' +import { lemonToast } from 'lib/lemon-ui/lemonToast' + import { actionsTabLogic } from '~/toolbar/actions/actionsTabLogic' import { toolbarButtonLogic } from '~/toolbar/button/toolbarButtonLogic' -import { lemonToast } from 'lib/lemon-ui/lemonToast' +import { posthog } from '~/toolbar/posthog' +import { clearSessionToolbarToken } from '~/toolbar/utils' +import { ToolbarProps } from '~/types' + +import type { toolbarLogicType } from './toolbarLogicType' export const toolbarLogic = kea([ path(['toolbar', 'toolbarLogic']), diff --git a/frontend/src/toolbar/utils.ts b/frontend/src/toolbar/utils.ts index dc5cf9a9353a9..90b37dde6431f 100644 --- a/frontend/src/toolbar/utils.ts +++ b/frontend/src/toolbar/utils.ts @@ -1,13 +1,14 @@ -import { cssEscape } from 'lib/utils/cssEscape' -import { ActionStepType, StringMatching } from '~/types' -import { ActionStepForm, BoxColor, ElementRect } from '~/toolbar/types' -import { querySelectorAllDeep } from 'query-selector-shadow-dom' -import { toolbarLogic } from '~/toolbar/toolbarLogic' +import { finder } from '@medv/finder' import { combineUrl, encodeParams } from 'kea-router' import { CLICK_TARGET_SELECTOR, CLICK_TARGETS, escapeRegex, TAGS_TO_IGNORE } from 'lib/actionUtils' -import { finder } from '@medv/finder' +import { cssEscape } from 'lib/utils/cssEscape' +import { querySelectorAllDeep } from 'query-selector-shadow-dom' import wildcardMatch from 'wildcard-match' +import { toolbarLogic } from '~/toolbar/toolbarLogic' +import { ActionStepForm, BoxColor, ElementRect } from '~/toolbar/types' +import { ActionStepType, StringMatching } from '~/types' + export function getSafeText(el: HTMLElement): string { if (!el.childNodes || !el.childNodes.length) { return '' diff --git a/frontend/src/types.ts b/frontend/src/types.ts index 1ba6f2e6157e8..bf9bba96c3194 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -1,3 +1,10 @@ +import { PluginConfigSchema } from '@posthog/plugin-scaffold' +import { eventWithTime } from '@rrweb/types' +import { UploadFile } from 'antd/lib/upload/interface' +import { ChartDataset, ChartType, InteractionItem } from 'chart.js' +import { LogicWrapper } from 'kea' +import { DashboardCompatibleScenes } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { BIN_COUNT_AUTO, DashboardPrivilegeLevel, @@ -12,20 +19,18 @@ import { ShownAsValue, TeamMembershipLevel, } from 'lib/constants' -import { PluginConfigSchema } from '@posthog/plugin-scaffold' -import { PluginInstallationType } from 'scenes/plugins/types' -import { UploadFile } from 'antd/lib/upload/interface' -import { eventWithTime } from '@rrweb/types' -import { PostHog } from 'posthog-js' -import { PopoverProps } from 'lib/lemon-ui/Popover/Popover' import { Dayjs, dayjs } from 'lib/dayjs' -import { ChartDataset, ChartType, InteractionItem } from 'chart.js' +import { PopoverProps } from 'lib/lemon-ui/Popover/Popover' +import { PostHog } from 'posthog-js' +import { Layout } from 'react-grid-layout' import { LogLevel } from 'rrweb' -import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { BehavioralFilterKey, BehavioralFilterType } from 'scenes/cohorts/CohortFilters/types' -import { LogicWrapper } from 'kea' import { AggregationAxisFormat } from 'scenes/insights/aggregationAxisFormat' -import { Layout } from 'react-grid-layout' +import { JSONContent } from 'scenes/notebooks/Notebook/utils' +import { PluginInstallationType } from 'scenes/plugins/types' + +import { QueryContext } from '~/queries/types' + import type { DashboardFilter, DatabaseSchemaQueryResponseField, @@ -33,10 +38,6 @@ import type { InsightVizNode, Node, } from './queries/schema' -import { QueryContext } from '~/queries/types' - -import { JSONContent } from 'scenes/notebooks/Notebook/utils' -import { DashboardCompatibleScenes } from 'lib/components/SceneDashboardChoice/sceneDashboardChoiceModalLogic' export type Optional = Omit & { [K in keyof T]?: T[K] } @@ -1151,6 +1152,10 @@ export interface PerformanceEvent { request_body?: Body response_body?: Body method?: string + + //rrweb/network@1 - i.e. not in ClickHouse table + is_initial?: boolean + raw?: Record } export interface CurrentBillCycleType { @@ -1754,6 +1759,7 @@ export interface TrendsFilterType extends FilterType { aggregation_axis_prefix?: string // a prefix to add to the aggregation axis e.g. £ aggregation_axis_postfix?: string // a postfix to add to the aggregation axis e.g. % show_values_on_series?: boolean + show_labels_on_series?: boolean show_percent_stack_view?: boolean } diff --git a/latest_migrations.manifest b/latest_migrations.manifest index 7ad1758c3c617..27497f398a6fa 100644 --- a/latest_migrations.manifest +++ b/latest_migrations.manifest @@ -5,7 +5,7 @@ contenttypes: 0002_remove_content_type_name ee: 0015_add_verified_properties otp_static: 0002_throttling otp_totp: 0002_auto_20190420_0723 -posthog: 0364_team_external_data_workspace_rows +posthog: 0366_alter_action_created_by sessions: 0001_initial social_django: 0010_uid_db_index two_factor: 0007_auto_20201201_1019 diff --git a/package.json b/package.json index b56889a11d530..194e4190efbee 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@medv/finder": "^2.1.0", "@microlink/react-json-view": "^1.21.3", "@monaco-editor/react": "4.4.6", - "@posthog/icons": "0.4.1", + "@posthog/icons": "0.4.11", "@posthog/plugin-scaffold": "^1.4.4", "@react-hook/size": "^2.1.2", "@rrweb/types": "^2.0.0-alpha.11", @@ -136,7 +136,7 @@ "monaco-editor": "^0.39.0", "papaparse": "^5.4.1", "pmtiles": "^2.11.0", - "posthog-js": "1.92.0", + "posthog-js": "1.92.1", "posthog-js-lite": "2.0.0-alpha5", "prettier": "^2.8.8", "prop-types": "^15.7.2", @@ -151,7 +151,7 @@ "react-dom": "^18.2.0", "react-draggable": "^4.2.0", "react-grid-layout": "^1.3.0", - "react-intersection-observer": "^9.4.3", + "react-intersection-observer": "^9.5.3", "react-markdown": "^5.0.3", "react-modal": "^3.15.1", "react-resizable": "^3.0.5", @@ -197,7 +197,7 @@ "@storybook/csf": "^0.1.1", "@storybook/react": "^7.5.1", "@storybook/react-webpack5": "^7.5.1", - "@storybook/test-runner": "^0.13.0", + "@storybook/test-runner": "^0.15.2", "@storybook/theming": "^7.5.1", "@storybook/types": "^7.5.1", "@sucrase/jest-plugin": "^3.0.0", @@ -251,6 +251,7 @@ "eslint-plugin-posthog": "link:./eslint-rules", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-react": "^7.33.2", + "eslint-plugin-simple-import-sort": "^10.0.0", "eslint-plugin-storybook": "^0.6.15", "file-loader": "^6.1.0", "givens": "^1.3.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e147d0d5b2a67..88e2b0d31b610 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,8 +36,8 @@ dependencies: specifier: 4.4.6 version: 4.4.6(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0) '@posthog/icons': - specifier: 0.4.1 - version: 0.4.1(react-dom@18.2.0)(react@18.2.0) + specifier: 0.4.11 + version: 0.4.11(react-dom@18.2.0)(react@18.2.0) '@posthog/plugin-scaffold': specifier: ^1.4.4 version: 1.4.4 @@ -216,8 +216,8 @@ dependencies: specifier: ^2.11.0 version: 2.11.0 posthog-js: - specifier: 1.92.0 - version: 1.92.0 + specifier: 1.92.1 + version: 1.92.1 posthog-js-lite: specifier: 2.0.0-alpha5 version: 2.0.0-alpha5 @@ -261,8 +261,8 @@ dependencies: specifier: ^1.3.0 version: 1.3.4(react-dom@18.2.0)(react@18.2.0) react-intersection-observer: - specifier: ^9.4.3 - version: 9.4.3(react@18.2.0) + specifier: ^9.5.3 + version: 9.5.3(react@18.2.0) react-markdown: specifier: ^5.0.3 version: 5.0.3(@types/react@17.0.52)(react@18.2.0) @@ -400,8 +400,8 @@ devDependencies: specifier: ^7.5.1 version: 7.5.1(@babel/core@7.22.10)(@swc/core@1.3.93)(esbuild@0.14.54)(react-dom@18.2.0)(react@18.2.0)(typescript@4.9.5)(webpack-cli@5.1.4) '@storybook/test-runner': - specifier: ^0.13.0 - version: 0.13.0(@types/node@18.11.9)(ts-node@10.9.1) + specifier: ^0.15.2 + version: 0.15.2(@types/node@18.11.9)(ts-node@10.9.1) '@storybook/theming': specifier: ^7.5.1 version: 7.5.1(react-dom@18.2.0)(react@18.2.0) @@ -552,6 +552,9 @@ devDependencies: eslint-plugin-react: specifier: ^7.33.2 version: 7.33.2(eslint@8.52.0) + eslint-plugin-simple-import-sort: + specifier: ^10.0.0 + version: 10.0.0(eslint@8.52.0) eslint-plugin-storybook: specifier: ^0.6.15 version: 0.6.15(eslint@8.52.0)(typescript@4.9.5) @@ -790,9 +793,9 @@ packages: resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 jsesc: 2.5.2 /@babel/helper-annotate-as-pure@7.22.5: @@ -805,7 +808,7 @@ packages: resolution: {integrity: sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 dev: true /@babel/helper-compilation-targets@7.22.10: @@ -870,19 +873,19 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-member-expression-to-functions@7.22.5: resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-module-imports@7.22.5: resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} @@ -907,7 +910,7 @@ packages: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-plugin-utils@7.22.5: resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} @@ -940,19 +943,19 @@ packages: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} @@ -961,7 +964,6 @@ packages: /@babel/helper-string-parser@7.23.4: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} - dev: true /@babel/helper-validator-identifier@7.22.20: resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} @@ -977,7 +979,7 @@ packages: dependencies: '@babel/helper-function-name': 7.23.0 '@babel/template': 7.22.15 - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 dev: true /@babel/helpers@7.22.10: @@ -1011,7 +1013,6 @@ packages: hasBin: true dependencies: '@babel/types': 7.23.4 - dev: true /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.10): resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==} @@ -2091,8 +2092,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.13 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.23.4 + '@babel/types': 7.23.4 /@babel/template@7.22.5: resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} @@ -2134,7 +2135,6 @@ packages: '@babel/helper-string-parser': 7.23.4 '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true /@base2/pretty-print-object@1.0.1: resolution: {integrity: sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==} @@ -2673,71 +2673,28 @@ packages: engines: {node: '>=8'} dev: true - /@jest/console@28.1.3: - resolution: {integrity: sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@types/node': 18.18.4 - chalk: 4.1.2 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - slash: 3.0.0 - dev: true - /@jest/console@29.3.1: resolution: {integrity: sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.18.4 chalk: 4.1.2 - jest-message-util: 29.3.1 - jest-util: 29.3.1 + jest-message-util: 29.7.0 + jest-util: 29.7.0 slash: 3.0.0 dev: true - /@jest/core@28.1.3(ts-node@10.9.1): - resolution: {integrity: sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + /@jest/console@29.7.0: + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 28.1.3 - '@jest/reporters': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 + '@jest/types': 29.6.3 '@types/node': 18.18.4 - ansi-escapes: 4.3.2 chalk: 4.1.2 - ci-info: 3.5.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 28.1.3 - jest-config: 28.1.3(@types/node@18.18.4)(ts-node@10.9.1) - jest-haste-map: 28.1.3 - jest-message-util: 28.1.3 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-resolve-dependencies: 28.1.3 - jest-runner: 28.1.3 - jest-runtime: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 - jest-validate: 28.1.3 - jest-watcher: 28.1.3 - micromatch: 4.0.5 - pretty-format: 28.1.3 - rimraf: 3.0.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - supports-color - - ts-node dev: true /@jest/core@29.3.1(ts-node@10.9.1): @@ -2778,6 +2735,50 @@ packages: slash: 3.0.0 strip-ansi: 6.0.1 transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + dev: true + + /@jest/core@29.7.0(ts-node@10.9.1): + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.4 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.5.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.18.4)(ts-node@10.9.1) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros - supports-color - ts-node dev: true @@ -2789,16 +2790,6 @@ packages: '@jest/types': 27.5.1 dev: true - /@jest/environment@28.1.3: - resolution: {integrity: sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/fake-timers': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.18.4 - jest-mock: 28.1.3 - dev: true - /@jest/environment@29.3.1: resolution: {integrity: sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2809,11 +2800,14 @@ packages: jest-mock: 29.3.1 dev: true - /@jest/expect-utils@28.1.3: - resolution: {integrity: sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/environment@29.7.0: + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 28.0.2 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.4 + jest-mock: 29.7.0 dev: true /@jest/expect-utils@29.3.1: @@ -2823,38 +2817,23 @@ packages: jest-get-type: 29.2.0 dev: true - /@jest/expect@28.1.3: - resolution: {integrity: sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/expect-utils@29.7.0: + resolution: {integrity: sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - expect: 28.1.3 - jest-snapshot: 28.1.3 - transitivePeerDependencies: - - supports-color + jest-get-type: 29.6.3 dev: true - /@jest/expect@29.3.1: - resolution: {integrity: sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==} + /@jest/expect@29.7.0: + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - expect: 29.3.1 - jest-snapshot: 29.3.1 + expect: 29.7.0 + jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color dev: true - /@jest/fake-timers@28.1.3: - resolution: {integrity: sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/types': 28.1.3 - '@sinonjs/fake-timers': 9.1.2 - '@types/node': 18.18.4 - jest-message-util: 28.1.3 - jest-mock: 28.1.3 - jest-util: 28.1.3 - dev: true - /@jest/fake-timers@29.3.1: resolution: {integrity: sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2867,32 +2846,45 @@ packages: jest-util: 29.3.1 dev: true - /@jest/globals@28.1.3: - resolution: {integrity: sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/fake-timers@29.7.0: + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 28.1.3 - '@jest/expect': 28.1.3 - '@jest/types': 28.1.3 - transitivePeerDependencies: - - supports-color + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 18.18.4 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 dev: true /@jest/globals@29.3.1: resolution: {integrity: sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.3.1 - '@jest/expect': 29.3.1 - '@jest/types': 29.3.1 - jest-mock: 29.3.1 + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + dev: true + + /@jest/globals@29.7.0: + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 transitivePeerDependencies: - supports-color dev: true - /@jest/reporters@28.1.3: - resolution: {integrity: sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/reporters@29.3.1: + resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -2900,11 +2892,11 @@ packages: optional: true dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.17 + '@jest/console': 29.3.1 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.3.1 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 '@types/node': 18.18.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 @@ -2916,20 +2908,19 @@ packages: istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - jest-worker: 28.1.3 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.3.1 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 - terminal-link: 2.1.1 v8-to-istanbul: 9.0.1 transitivePeerDependencies: - supports-color dev: true - /@jest/reporters@29.3.1: - resolution: {integrity: sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==} + /@jest/reporters@29.7.0: + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -2938,11 +2929,11 @@ packages: optional: true dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@jridgewell/trace-mapping': 0.3.17 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 '@types/node': 18.18.4 chalk: 4.1.2 collect-v8-coverage: 1.0.1 @@ -2950,13 +2941,13 @@ packages: glob: 7.2.3 graceful-fs: 4.2.11 istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 + istanbul-lib-instrument: 6.0.1 istanbul-lib-report: 3.0.0 istanbul-lib-source-maps: 4.0.1 istanbul-reports: 3.1.5 - jest-message-util: 29.3.1 - jest-util: 29.3.1 - jest-worker: 29.3.1 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 slash: 3.0.0 string-length: 4.0.2 strip-ansi: 6.0.1 @@ -2965,13 +2956,6 @@ packages: - supports-color dev: true - /@jest/schemas@28.1.3: - resolution: {integrity: sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@sinclair/typebox': 0.24.51 - dev: true - /@jest/schemas@29.0.0: resolution: {integrity: sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -2979,32 +2963,29 @@ packages: '@sinclair/typebox': 0.24.51 dev: true - /@jest/source-map@28.1.2: - resolution: {integrity: sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jridgewell/trace-mapping': 0.3.17 - callsites: 3.1.0 - graceful-fs: 4.2.11 + '@sinclair/typebox': 0.27.8 dev: true /@jest/source-map@29.2.0: resolution: {integrity: sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 callsites: 3.1.0 graceful-fs: 4.2.11 dev: true - /@jest/test-result@28.1.3: - resolution: {integrity: sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/source-map@29.6.3: + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 28.1.3 - '@jest/types': 28.1.3 - '@types/istanbul-lib-coverage': 2.0.4 - collect-v8-coverage: 1.0.1 + '@jridgewell/trace-mapping': 0.3.20 + callsites: 3.1.0 + graceful-fs: 4.2.11 dev: true /@jest/test-result@29.3.1: @@ -3012,46 +2993,56 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/console': 29.3.1 - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 collect-v8-coverage: 1.0.1 dev: true - /@jest/test-sequencer@28.1.3: - resolution: {integrity: sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/test-result@29.7.0: + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 28.1.3 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - slash: 3.0.0 + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.4 + collect-v8-coverage: 1.0.1 dev: true /@jest/test-sequencer@29.3.1: resolution: {integrity: sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.3.1 + '@jest/test-result': 29.7.0 graceful-fs: 4.2.11 - jest-haste-map: 29.3.1 + jest-haste-map: 29.7.0 slash: 3.0.0 dev: true - /@jest/transform@28.1.3: - resolution: {integrity: sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/test-sequencer@29.7.0: + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + dev: true + + /@jest/transform@29.3.1: + resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.22.10 - '@jest/types': 28.1.3 - '@jridgewell/trace-mapping': 0.3.17 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 - convert-source-map: 1.9.0 + convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-regex-util: 28.0.2 - jest-util: 28.1.3 + jest-haste-map: 29.3.1 + jest-regex-util: 29.2.0 + jest-util: 29.3.1 micromatch: 4.0.5 pirates: 4.0.5 slash: 3.0.0 @@ -3060,21 +3051,21 @@ packages: - supports-color dev: true - /@jest/transform@29.3.1: - resolution: {integrity: sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==} + /@jest/transform@29.7.0: + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.22.10 - '@jest/types': 29.3.1 - '@jridgewell/trace-mapping': 0.3.17 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.20 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 29.3.1 - jest-regex-util: 29.2.0 - jest-util: 29.3.1 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 micromatch: 4.0.5 pirates: 4.0.5 slash: 3.0.0 @@ -3094,11 +3085,11 @@ packages: chalk: 4.1.2 dev: true - /@jest/types@28.1.3: - resolution: {integrity: sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /@jest/types@29.3.1: + resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 28.1.3 + '@jest/schemas': 29.0.0 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 '@types/node': 18.18.4 @@ -3106,11 +3097,11 @@ packages: chalk: 4.1.2 dev: true - /@jest/types@29.3.1: - resolution: {integrity: sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==} + /@jest/types@29.6.3: + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.0.0 + '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.4 '@types/istanbul-reports': 3.0.1 '@types/node': 18.18.4 @@ -3131,7 +3122,7 @@ packages: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} @@ -3145,7 +3136,7 @@ packages: resolution: {integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==} dependencies: '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 dev: true /@jridgewell/sourcemap-codec@1.4.14: @@ -3157,6 +3148,12 @@ packages: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + /@jridgewell/trace-mapping@0.3.20: + resolution: {integrity: sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: @@ -3422,8 +3419,8 @@ packages: resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} dev: false - /@posthog/icons@0.4.1(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-sR7lDltjoAeExsOMZOZvCz8Z1rHbBqhZo8RABCCvx00MoBCUuydE1y2xpSoP5BVfMogY4ycDktnihw4ICUsb3Q==} + /@posthog/icons@0.4.11(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-hpHDrBIZlnc4Z0d8BPjNkMf7gkmR3o7CQGcvXJ8fQzyOL07EU3I0LRfkRHhFaRaZdeDB4TkJkkSDJ8beyuhBPQ==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0' @@ -4136,6 +4133,10 @@ packages: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + /@sinonjs/commons@1.8.4: resolution: {integrity: sha512-RpmQdHVo8hCEHDVpO39zToS9jOhR6nw+/lQAzRNq9ErrGV9IeHM71XCn68svVl/euFeVW6BWX4p35gkhbOcSIQ==} deprecated: Breaks compatibility with ES5, use v1.8.5 @@ -4143,6 +4144,18 @@ packages: type-detect: 4.0.8 dev: true + /@sinonjs/commons@3.0.0: + resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + dependencies: + type-detect: 4.0.8 + dev: true + + /@sinonjs/fake-timers@10.3.0: + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + dependencies: + '@sinonjs/commons': 3.0.0 + dev: true + /@sinonjs/fake-timers@9.1.2: resolution: {integrity: sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==} dependencies: @@ -4738,7 +4751,7 @@ packages: dependencies: '@babel/core': 7.22.10 '@babel/preset-env': 7.22.10(@babel/core@7.22.10) - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 '@storybook/csf': 0.1.1 '@storybook/csf-tools': 7.5.1 '@storybook/node-logger': 7.5.1 @@ -4904,9 +4917,9 @@ packages: resolution: {integrity: sha512-YChGbT1/odLS4RLb2HtK7ixM7mH5s7G5nOsWGKXalbza4SFKZIU2UzllEUsA+X8YfxMHnCD5TC3xLfK0ByxmzQ==} dependencies: '@babel/generator': 7.23.0 - '@babel/parser': 7.23.0 + '@babel/parser': 7.23.4 '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 '@storybook/csf': 0.1.1 '@storybook/types': 7.5.1 fs-extra: 11.1.1 @@ -5271,32 +5284,33 @@ packages: - supports-color dev: true - /@storybook/test-runner@0.13.0(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-QIbfgia/iBy7PeUIwCYtPcyeZCHd21ebaPoMNIsRfwUW+VC12J4iG8cGDfOE7MGbMVz1Uu0elAEBB8NGP/YBtQ==} + /@storybook/test-runner@0.15.2(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-nHwThLvxho9wNAVxtESoAcrQD7UolOAJISwcG9uz3bmtTIm7h5DMlpfX+2DKbJyq5REg8nhcauZv5iFvwBdn1Q==} + engines: {node: ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true dependencies: '@babel/core': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/template': 7.22.5 - '@babel/types': 7.23.0 + '@babel/generator': 7.23.0 + '@babel/template': 7.22.15 + '@babel/types': 7.23.4 '@storybook/core-common': 7.5.1 '@storybook/csf': 0.1.1 '@storybook/csf-tools': 7.5.1 - '@storybook/preview-api': 7.5.1 + '@storybook/preview-api': 7.5.3 '@swc/core': 1.3.93 '@swc/jest': 0.2.29(@swc/core@1.3.93) can-bind-to-host: 1.1.2 commander: 9.4.1 expect-playwright: 0.8.0 glob: 10.3.3 - jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1) - jest-circus: 28.1.3 - jest-environment-node: 28.1.3 - jest-junit: 14.0.1 - jest-playwright-preset: 2.0.0(jest-circus@28.1.3)(jest-environment-node@28.1.3)(jest-runner@28.1.3)(jest@28.1.3) - jest-runner: 28.1.3 + jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-junit: 16.0.0 + jest-playwright-preset: 3.0.1(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0) + jest-runner: 29.7.0 jest-serializer-html: 7.1.0 - jest-watch-typeahead: 2.2.2(jest@28.1.3) + jest-watch-typeahead: 2.2.2(jest@29.7.0) node-fetch: 2.6.7 playwright: 1.29.2 read-pkg-up: 7.0.1 @@ -5305,6 +5319,7 @@ packages: transitivePeerDependencies: - '@swc/helpers' - '@types/node' + - babel-plugin-macros - debug - encoding - node-notifier @@ -5911,7 +5926,7 @@ packages: /@types/babel__generator@7.6.6: resolution: {integrity: sha512-66BXMKb/sUWbMdBNdMvajU7i/44RkrA3z/Yt1c7R5xejt8qh84iU54yUWCtm0QwGJlDcf/gg4zd/x4mpLAlb/w==} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 dev: true /@types/babel__generator@7.6.7: @@ -5923,8 +5938,8 @@ packages: /@types/babel__template@7.4.3: resolution: {integrity: sha512-ciwyCLeuRfxboZ4isgdNZi/tkt06m8Tw6uGbBSBgWrnnZGNXiEyM27xc/PjXGQLqlZ6ylbgHMnm7ccF9tCkOeQ==} dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/parser': 7.23.4 + '@babel/types': 7.23.4 dev: true /@types/babel__template@7.4.4: @@ -5937,7 +5952,7 @@ packages: /@types/babel__traverse@7.20.3: resolution: {integrity: sha512-Lsh766rGEFbaxMIDH7Qa+Yha8cMVI3qAK6CHt3OR0YfxOIn5Z54iHiyDRycHrBqeIiqGa20Kpsv1cavfBKkRSw==} dependencies: - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 dev: true /@types/babel__traverse@7.20.4: @@ -5957,7 +5972,7 @@ packages: resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} dependencies: '@types/connect': 3.4.38 - '@types/node': 18.11.9 + '@types/node': 18.18.4 dev: true /@types/chart.js@2.9.37: @@ -5985,7 +6000,7 @@ packages: /@types/connect@3.4.38: resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} dependencies: - '@types/node': 18.11.9 + '@types/node': 18.18.4 dev: true /@types/cookie@0.4.1: @@ -6263,7 +6278,7 @@ packages: /@types/express-serve-static-core@4.17.41: resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} dependencies: - '@types/node': 18.11.9 + '@types/node': 18.18.4 '@types/qs': 6.9.10 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -6597,7 +6612,7 @@ packages: resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} dependencies: '@types/mime': 1.3.5 - '@types/node': 18.11.9 + '@types/node': 18.18.4 dev: true /@types/serve-static@1.15.4: @@ -6613,7 +6628,7 @@ packages: dependencies: '@types/http-errors': 2.0.4 '@types/mime': 3.0.4 - '@types/node': 18.11.9 + '@types/node': 18.18.4 dev: true /@types/set-cookie-parser@2.4.2: @@ -7627,17 +7642,17 @@ packages: '@babel/core': 7.22.10 dev: true - /babel-jest@28.1.3(@babel/core@7.22.10): - resolution: {integrity: sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /babel-jest@29.3.1(@babel/core@7.22.10): + resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: '@babel/core': 7.22.10 - '@jest/transform': 28.1.3 - '@types/babel__core': 7.20.3 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.4 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 28.1.3(@babel/core@7.22.10) + babel-preset-jest: 29.2.0(@babel/core@7.22.10) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -7645,17 +7660,17 @@ packages: - supports-color dev: true - /babel-jest@29.3.1(@babel/core@7.22.10): - resolution: {integrity: sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==} + /babel-jest@29.7.0(@babel/core@7.22.10): + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.8.0 dependencies: '@babel/core': 7.22.10 - '@jest/transform': 29.3.1 - '@types/babel__core': 7.20.3 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.4 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.2.0(@babel/core@7.22.10) + babel-preset-jest: 29.6.3(@babel/core@7.22.10) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -7714,24 +7729,24 @@ packages: - supports-color dev: true - /babel-plugin-jest-hoist@28.1.3: - resolution: {integrity: sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /babel-plugin-jest-hoist@29.2.0: + resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - '@types/babel__core': 7.20.3 - '@types/babel__traverse': 7.20.3 + '@babel/types': 7.23.4 + '@types/babel__core': 7.20.4 + '@types/babel__traverse': 7.20.4 dev: true - /babel-plugin-jest-hoist@29.2.0: - resolution: {integrity: sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==} + /babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - '@types/babel__core': 7.20.3 - '@types/babel__traverse': 7.20.3 + '@babel/types': 7.23.4 + '@types/babel__core': 7.20.4 + '@types/babel__traverse': 7.20.4 dev: true /babel-plugin-named-exports-order@0.0.2: @@ -7801,25 +7816,25 @@ packages: '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.22.10) dev: true - /babel-preset-jest@28.1.3(@babel/core@7.22.10): - resolution: {integrity: sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /babel-preset-jest@29.2.0(@babel/core@7.22.10): + resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.22.10 - babel-plugin-jest-hoist: 28.1.3 + babel-plugin-jest-hoist: 29.2.0 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) dev: true - /babel-preset-jest@29.2.0(@babel/core@7.22.10): - resolution: {integrity: sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==} + /babel-preset-jest@29.6.3(@babel/core@7.22.10): + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@babel/core': ^7.0.0 dependencies: '@babel/core': 7.22.10 - babel-plugin-jest-hoist: 29.2.0 + babel-plugin-jest-hoist: 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) dev: true @@ -8691,6 +8706,25 @@ packages: typescript: 4.9.5 dev: true + /create-jest@29.7.0(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + dev: true + /create-require@1.1.1: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true @@ -9385,6 +9419,15 @@ packages: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} dev: true + /dedent@1.5.1: + resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + dev: true + /deep-equal@2.1.0: resolution: {integrity: sha512-2pxgvWu3Alv1PoWEyVg7HS8YhGlUFUV7N5oOvfL6d+7xAmLSemMwv/c8Zv/i9KFzxV5Kt5CAvQc70fLwVuf4UA==} dependencies: @@ -9550,16 +9593,16 @@ packages: - supports-color dev: true - /diff-sequences@28.1.1: - resolution: {integrity: sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /diff-sequences@29.3.1: resolution: {integrity: sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + /diff@4.0.2: resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} @@ -9758,11 +9801,6 @@ packages: /electron-to-chromium@1.4.492: resolution: {integrity: sha512-36K9b/6skMVwAIEsC7GiQ8I8N3soCALVSHqWHzNDtGemAcI9Xu8hP02cywWM0A794rTHm0b0zHPeLJHtgFVamQ==} - /emittery@0.10.2: - resolution: {integrity: sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==} - engines: {node: '>=12'} - dev: true - /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} @@ -10411,6 +10449,14 @@ packages: string.prototype.matchall: 4.0.8 dev: true + /eslint-plugin-simple-import-sort@10.0.0(eslint@8.52.0): + resolution: {integrity: sha512-AeTvO9UCMSNzIHRkg8S6c3RPy5YEwKWSQPx3DYghLedo2ZQxowPFLGDN1AZ2evfg6r6mjBSZSLxLFsWSu3acsw==} + peerDependencies: + eslint: '>=5.0.0' + dependencies: + eslint: 8.52.0 + dev: true + /eslint-plugin-storybook@0.6.15(eslint@8.52.0)(typescript@4.9.5): resolution: {integrity: sha512-lAGqVAJGob47Griu29KXYowI4G7KwMoJDOkEip8ujikuDLxU+oWJ1l0WL6F2oDO4QiyUFXvtDkEkISMOPzo+7w==} engines: {node: 12.x || 14.x || >= 16} @@ -10539,7 +10585,7 @@ packages: engines: {node: '>=8.3.0'} dependencies: '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 + '@babel/types': 7.23.4 c8: 7.14.0 transitivePeerDependencies: - supports-color @@ -10635,17 +10681,6 @@ packages: resolution: {integrity: sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg==} dev: true - /expect@28.1.3: - resolution: {integrity: sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/expect-utils': 28.1.3 - jest-get-type: 28.0.2 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-util: 28.1.3 - dev: true - /expect@29.3.1: resolution: {integrity: sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -10657,6 +10692,17 @@ packages: jest-util: 29.3.1 dev: true + /expect@29.7.0: + resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/expect-utils': 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + dev: true + /expr-eval@2.0.2: resolution: {integrity: sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==} dev: false @@ -12461,7 +12507,7 @@ packages: engines: {node: '>=8'} dependencies: '@babel/core': 7.22.10 - '@babel/parser': 7.23.0 + '@babel/parser': 7.23.4 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.1 @@ -12469,6 +12515,19 @@ packages: - supports-color dev: true + /istanbul-lib-instrument@6.0.1: + resolution: {integrity: sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==} + engines: {node: '>=10'} + dependencies: + '@babel/core': 7.22.10 + '@babel/parser': 7.23.4 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.0 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + dev: true + /istanbul-lib-processinfo@2.0.3: resolution: {integrity: sha512-NkwHbo3E00oybX6NGJi6ar0B29vxyvNwoC7eJ4G4Yq28UfY758Hgn/heV8VRFhevPED4LXfFz0DQ8z/0kw9zMg==} engines: {node: '>=8'} @@ -12546,14 +12605,6 @@ packages: moo-color: 1.0.3 dev: true - /jest-changed-files@28.1.3: - resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - execa: 5.1.1 - p-limit: 3.1.0 - dev: true - /jest-changed-files@29.2.0: resolution: {integrity: sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -12562,63 +12613,47 @@ packages: p-limit: 3.1.0 dev: true - /jest-circus@28.1.3: - resolution: {integrity: sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 28.1.3 - '@jest/expect': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.18.4 - chalk: 4.1.2 - co: 4.6.0 - dedent: 0.7.0 - is-generator-fn: 2.1.0 - jest-each: 28.1.3 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-runtime: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 + execa: 5.1.1 + jest-util: 29.7.0 p-limit: 3.1.0 - pretty-format: 28.1.3 - slash: 3.0.0 - stack-utils: 2.0.5 - transitivePeerDependencies: - - supports-color dev: true - /jest-circus@29.3.1: - resolution: {integrity: sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==} + /jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.3.1 - '@jest/expect': 29.3.1 - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.18.4 chalk: 4.1.2 co: 4.6.0 - dedent: 0.7.0 + dedent: 1.5.1 is-generator-fn: 2.1.0 - jest-each: 29.3.1 - jest-matcher-utils: 29.3.1 - jest-message-util: 29.3.1 - jest-runtime: 29.3.1 - jest-snapshot: 29.3.1 - jest-util: 29.3.1 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 p-limit: 3.1.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 + pure-rand: 6.0.4 slash: 3.0.0 stack-utils: 2.0.5 transitivePeerDependencies: + - babel-plugin-macros - supports-color dev: true - /jest-cli@28.1.3(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-cli@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -12626,26 +12661,27 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 28.1.3(ts-node@10.9.1) - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 + '@jest/core': 29.3.1(ts-node@10.9.1) + '@jest/test-result': 29.3.1 + '@jest/types': 29.3.1 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1) - jest-util: 28.1.3 - jest-validate: 28.1.3 + jest-config: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1) + jest-util: 29.3.1 + jest-validate: 29.3.1 prompts: 2.4.2 yargs: 17.6.2 transitivePeerDependencies: - '@types/node' + - babel-plugin-macros - supports-color - ts-node dev: true - /jest-cli@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==} + /jest-cli@29.7.0(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -12654,27 +12690,27 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.3.1(ts-node@10.9.1) - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) exit: 0.1.2 - graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1) - jest-util: 29.3.1 - jest-validate: 29.3.1 - prompts: 2.4.2 + jest-config: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) + jest-util: 29.7.0 + jest-validate: 29.7.0 yargs: 17.6.2 transitivePeerDependencies: - '@types/node' + - babel-plugin-macros - supports-color - ts-node dev: true - /jest-config@28.1.3(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-config@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' ts-node: '>=9.0.0' @@ -12685,36 +12721,37 @@ packages: optional: true dependencies: '@babel/core': 7.22.10 - '@jest/test-sequencer': 28.1.3 - '@jest/types': 28.1.3 + '@jest/test-sequencer': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.11.9 - babel-jest: 28.1.3(@babel/core@7.22.10) + babel-jest: 29.3.1(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 28.1.3 - jest-environment-node: 28.1.3 - jest-get-type: 28.0.2 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-runner: 28.1.3 - jest-util: 28.1.3 - jest-validate: 28.1.3 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.2.0 + jest-regex-util: 29.2.0 + jest-resolve: 29.3.1 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.3.1 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 28.1.3 + pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5) transitivePeerDependencies: + - babel-plugin-macros - supports-color dev: true - /jest-config@28.1.3(@types/node@18.18.4)(ts-node@10.9.1): - resolution: {integrity: sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-config@29.3.1(@types/node@18.18.4)(ts-node@10.9.1): + resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' ts-node: '>=9.0.0' @@ -12725,35 +12762,36 @@ packages: optional: true dependencies: '@babel/core': 7.22.10 - '@jest/test-sequencer': 28.1.3 - '@jest/types': 28.1.3 + '@jest/test-sequencer': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.18.4 - babel-jest: 28.1.3(@babel/core@7.22.10) + babel-jest: 29.3.1(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 28.1.3 - jest-environment-node: 28.1.3 - jest-get-type: 28.0.2 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-runner: 28.1.3 - jest-util: 28.1.3 - jest-validate: 28.1.3 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.2.0 + jest-regex-util: 29.2.0 + jest-resolve: 29.3.1 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.3.1 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 28.1.3 + pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5) transitivePeerDependencies: + - babel-plugin-macros - supports-color dev: true - /jest-config@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} + /jest-config@29.7.0(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' @@ -12765,35 +12803,36 @@ packages: optional: true dependencies: '@babel/core': 7.22.10 - '@jest/test-sequencer': 29.3.1 - '@jest/types': 29.3.1 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.11.9 - babel-jest: 29.3.1(@babel/core@7.22.10) + babel-jest: 29.7.0(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 29.3.1 - jest-environment-node: 29.3.1 - jest-get-type: 29.2.0 - jest-regex-util: 29.2.0 - jest-resolve: 29.3.1 - jest-runner: 29.3.1 - jest-util: 29.3.1 - jest-validate: 29.3.1 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5) transitivePeerDependencies: + - babel-plugin-macros - supports-color dev: true - /jest-config@29.3.1(@types/node@18.18.4)(ts-node@10.9.1): - resolution: {integrity: sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==} + /jest-config@29.7.0(@types/node@18.18.4)(ts-node@10.9.1): + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: '@types/node': '*' @@ -12805,43 +12844,34 @@ packages: optional: true dependencies: '@babel/core': 7.22.10 - '@jest/test-sequencer': 29.3.1 - '@jest/types': 29.3.1 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.18.4 - babel-jest: 29.3.1(@babel/core@7.22.10) + babel-jest: 29.7.0(@babel/core@7.22.10) chalk: 4.1.2 ci-info: 3.5.0 deepmerge: 4.2.2 glob: 7.2.3 graceful-fs: 4.2.11 - jest-circus: 29.3.1 - jest-environment-node: 29.3.1 - jest-get-type: 29.2.0 - jest-regex-util: 29.2.0 - jest-resolve: 29.3.1 - jest-runner: 29.3.1 - jest-util: 29.3.1 - jest-validate: 29.3.1 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 ts-node: 10.9.1(@swc/core@1.3.93)(@types/node@18.11.9)(typescript@4.9.5) transitivePeerDependencies: + - babel-plugin-macros - supports-color dev: true - /jest-diff@28.1.3: - resolution: {integrity: sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - chalk: 4.1.2 - diff-sequences: 28.1.1 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-diff@29.3.1: resolution: {integrity: sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -12849,14 +12879,17 @@ packages: chalk: 4.1.2 diff-sequences: 29.3.1 jest-get-type: 29.2.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 dev: true - /jest-docblock@28.1.1: - resolution: {integrity: sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-diff@29.7.0: + resolution: {integrity: sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - detect-newline: 3.1.0 + chalk: 4.1.2 + diff-sequences: 29.6.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true /jest-docblock@29.2.0: @@ -12866,26 +12899,22 @@ packages: detect-newline: 3.1.0 dev: true - /jest-each@28.1.3: - resolution: {integrity: sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 28.1.3 - chalk: 4.1.2 - jest-get-type: 28.0.2 - jest-util: 28.1.3 - pretty-format: 28.1.3 + detect-newline: 3.1.0 dev: true - /jest-each@29.3.1: - resolution: {integrity: sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==} + /jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 chalk: 4.1.2 - jest-get-type: 29.2.0 - jest-util: 29.3.1 - pretty-format: 29.3.1 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 dev: true /jest-environment-jsdom@29.3.1: @@ -12911,33 +12940,16 @@ packages: - utf-8-validate dev: true - /jest-environment-node@28.1.3: - resolution: {integrity: sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - '@jest/environment': 28.1.3 - '@jest/fake-timers': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.18.4 - jest-mock: 28.1.3 - jest-util: 28.1.3 - dev: true - - /jest-environment-node@29.3.1: - resolution: {integrity: sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==} + /jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.3.1 - '@jest/fake-timers': 29.3.1 - '@jest/types': 29.3.1 + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.18.4 - jest-mock: 29.3.1 - jest-util: 29.3.1 - dev: true - - /jest-get-type@28.0.2: - resolution: {integrity: sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + jest-mock: 29.7.0 + jest-util: 29.7.0 dev: true /jest-get-type@29.2.0: @@ -12945,38 +12957,43 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-haste-map@28.1.3: - resolution: {integrity: sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-get-type@29.6.3: + resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /jest-haste-map@29.3.1: + resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 28.1.3 + '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.5 '@types/node': 18.18.4 anymatch: 3.1.2 fb-watchman: 2.0.2 graceful-fs: 4.2.11 - jest-regex-util: 28.0.2 - jest-util: 28.1.3 - jest-worker: 28.1.3 + jest-regex-util: 29.2.0 + jest-util: 29.7.0 + jest-worker: 29.3.1 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 dev: true - /jest-haste-map@29.3.1: - resolution: {integrity: sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==} + /jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.5 '@types/node': 18.18.4 anymatch: 3.1.2 fb-watchman: 2.0.2 graceful-fs: 4.2.11 - jest-regex-util: 29.2.0 - jest-util: 29.3.1 - jest-worker: 29.3.1 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 micromatch: 4.0.5 walker: 1.0.8 optionalDependencies: @@ -13001,8 +13018,8 @@ packages: ssim.js: 3.5.0 dev: true - /jest-junit@14.0.1: - resolution: {integrity: sha512-h7/wwzPbllgpQhhVcRzRC76/cc89GlazThoV1fDxcALkf26IIlRsu/AcTG64f4nR2WPE3Cbd+i/sVf+NCUHrWQ==} + /jest-junit@16.0.0: + resolution: {integrity: sha512-A94mmw6NfJab4Fg/BlvVOUXzXgF0XIH6EmTgJ5NDPp4xoKq0Kr7sErb+4Xs9nZvu58pJojz5RFGpqnZYJTrRfQ==} engines: {node: '>=10.12.0'} dependencies: mkdirp: 1.0.4 @@ -13011,30 +13028,20 @@ packages: xml: 1.0.1 dev: true - /jest-leak-detector@28.1.3: - resolution: {integrity: sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - jest-get-type: 28.0.2 - pretty-format: 28.1.3 - dev: true - /jest-leak-detector@29.3.1: resolution: {integrity: sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - jest-get-type: 29.2.0 - pretty-format: 29.3.1 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true - /jest-matcher-utils@28.1.3: - resolution: {integrity: sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - chalk: 4.1.2 - jest-diff: 28.1.3 - jest-get-type: 28.0.2 - pretty-format: 28.1.3 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true /jest-matcher-utils@29.3.1: @@ -13047,19 +13054,14 @@ packages: pretty-format: 29.3.1 dev: true - /jest-message-util@28.1.3: - resolution: {integrity: sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-matcher-utils@29.7.0: + resolution: {integrity: sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@babel/code-frame': 7.22.13 - '@jest/types': 28.1.3 - '@types/stack-utils': 2.0.1 chalk: 4.1.2 - graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 28.1.3 - slash: 3.0.0 - stack-utils: 2.0.5 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + pretty-format: 29.7.0 dev: true /jest-message-util@29.3.1: @@ -13067,7 +13069,7 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/code-frame': 7.22.13 - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/stack-utils': 2.0.1 chalk: 4.1.2 graceful-fs: 4.2.11 @@ -13077,12 +13079,19 @@ packages: stack-utils: 2.0.5 dev: true - /jest-mock@28.1.3: - resolution: {integrity: sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-message-util@29.7.0: + resolution: {integrity: sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 28.1.3 - '@types/node': 18.18.4 + '@babel/code-frame': 7.22.13 + '@jest/types': 29.6.3 + '@types/stack-utils': 2.0.1 + chalk: 4.1.2 + graceful-fs: 4.2.11 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + stack-utils: 2.0.5 dev: true /jest-mock@29.3.1: @@ -13094,20 +13103,29 @@ packages: jest-util: 29.3.1 dev: true - /jest-playwright-preset@2.0.0(jest-circus@28.1.3)(jest-environment-node@28.1.3)(jest-runner@28.1.3)(jest@28.1.3): - resolution: {integrity: sha512-pV5ruTJJMen3lwshUL4dlSqLlP8z4q9MXqWJkmy+sB6HYfzXoqBHzhl+5hslznhnSVTe4Dwu+reiiwcUJpYUbw==} + /jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/types': 29.6.3 + '@types/node': 18.18.4 + jest-util: 29.7.0 + dev: true + + /jest-playwright-preset@3.0.1(jest-circus@29.7.0)(jest-environment-node@29.7.0)(jest-runner@29.7.0)(jest@29.7.0): + resolution: {integrity: sha512-tHqv+JUmheNMZpmH7XyT5CAMHr3ExTUIY9baMPzcJiLYPvCaPTwig9YvuGGnXV2n+Epmch0Ld4429g6py0nq0w==} peerDependencies: - jest: ^28.0.0 - jest-circus: ^28.0.0 - jest-environment-node: ^28.0.0 - jest-runner: ^28.0.0 + jest: ^29.3.1 + jest-circus: ^29.3.1 + jest-environment-node: ^29.3.1 + jest-runner: ^29.3.1 dependencies: expect-playwright: 0.8.0 - jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1) - jest-circus: 28.1.3 - jest-environment-node: 28.1.3 + jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 jest-process-manager: 0.3.1 - jest-runner: 28.1.3 + jest-runner: 29.7.0 nyc: 15.1.0 playwright-core: 1.29.2 rimraf: 3.0.2 @@ -13117,7 +13135,7 @@ packages: - supports-color dev: true - /jest-pnp-resolver@1.2.2(jest-resolve@28.1.3): + /jest-pnp-resolver@1.2.2(jest-resolve@29.3.1): resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} peerDependencies: @@ -13126,10 +13144,10 @@ packages: jest-resolve: optional: true dependencies: - jest-resolve: 28.1.3 + jest-resolve: 29.3.1 dev: true - /jest-pnp-resolver@1.2.2(jest-resolve@29.3.1): + /jest-pnp-resolver@1.2.2(jest-resolve@29.7.0): resolution: {integrity: sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==} engines: {node: '>=6'} peerDependencies: @@ -13138,7 +13156,7 @@ packages: jest-resolve: optional: true dependencies: - jest-resolve: 29.3.1 + jest-resolve: 29.7.0 dev: true /jest-process-manager@0.3.1: @@ -13159,24 +13177,14 @@ packages: - supports-color dev: true - /jest-regex-util@28.0.2: - resolution: {integrity: sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dev: true - /jest-regex-util@29.2.0: resolution: {integrity: sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true - /jest-resolve-dependencies@28.1.3: - resolution: {integrity: sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} - dependencies: - jest-regex-util: 28.0.2 - jest-snapshot: 28.1.3 - transitivePeerDependencies: - - supports-color + /jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dev: true /jest-resolve-dependencies@29.3.1: @@ -13184,24 +13192,19 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: jest-regex-util: 29.2.0 - jest-snapshot: 29.3.1 + jest-snapshot: 29.7.0 transitivePeerDependencies: - supports-color dev: true - /jest-resolve@28.1.3: - resolution: {integrity: sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - chalk: 4.1.2 - graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-pnp-resolver: 1.2.2(jest-resolve@28.1.3) - jest-util: 28.1.3 - jest-validate: 28.1.3 - resolve: 1.22.1 - resolve.exports: 1.1.0 - slash: 3.0.0 + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color dev: true /jest-resolve@29.3.1: @@ -13212,40 +13215,26 @@ packages: graceful-fs: 4.2.11 jest-haste-map: 29.3.1 jest-pnp-resolver: 1.2.2(jest-resolve@29.3.1) - jest-util: 29.3.1 + jest-util: 29.7.0 jest-validate: 29.3.1 resolve: 1.22.1 resolve.exports: 1.1.0 slash: 3.0.0 dev: true - /jest-runner@28.1.3: - resolution: {integrity: sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/console': 28.1.3 - '@jest/environment': 28.1.3 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 - '@types/node': 18.18.4 chalk: 4.1.2 - emittery: 0.10.2 graceful-fs: 4.2.11 - jest-docblock: 28.1.1 - jest-environment-node: 28.1.3 - jest-haste-map: 28.1.3 - jest-leak-detector: 28.1.3 - jest-message-util: 28.1.3 - jest-resolve: 28.1.3 - jest-runtime: 28.1.3 - jest-util: 28.1.3 - jest-watcher: 28.1.3 - jest-worker: 28.1.3 - p-limit: 3.1.0 - source-map-support: 0.5.13 - transitivePeerDependencies: - - supports-color + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.2(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.1 + resolve.exports: 2.0.2 + slash: 3.0.0 dev: true /jest-runner@29.3.1: @@ -13253,22 +13242,22 @@ packages: engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@jest/console': 29.3.1 - '@jest/environment': 29.3.1 - '@jest/test-result': 29.3.1 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.18.4 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 jest-docblock: 29.2.0 - jest-environment-node: 29.3.1 + jest-environment-node: 29.7.0 jest-haste-map: 29.3.1 jest-leak-detector: 29.3.1 - jest-message-util: 29.3.1 + jest-message-util: 29.7.0 jest-resolve: 29.3.1 - jest-runtime: 29.3.1 - jest-util: 29.3.1 + jest-runtime: 29.7.0 + jest-util: 29.7.0 jest-watcher: 29.3.1 jest-worker: 29.3.1 p-limit: 3.1.0 @@ -13277,32 +13266,31 @@ packages: - supports-color dev: true - /jest-runtime@28.1.3: - resolution: {integrity: sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 28.1.3 - '@jest/fake-timers': 28.1.3 - '@jest/globals': 28.1.3 - '@jest/source-map': 28.1.2 - '@jest/test-result': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.4 chalk: 4.1.2 - cjs-module-lexer: 1.2.2 - collect-v8-coverage: 1.0.1 - execa: 5.1.1 - glob: 7.2.3 + emittery: 0.13.1 graceful-fs: 4.2.11 - jest-haste-map: 28.1.3 - jest-message-util: 28.1.3 - jest-mock: 28.1.3 - jest-regex-util: 28.0.2 - jest-resolve: 28.1.3 - jest-snapshot: 28.1.3 - jest-util: 28.1.3 - slash: 3.0.0 - strip-bom: 4.0.0 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 transitivePeerDependencies: - supports-color dev: true @@ -13311,13 +13299,13 @@ packages: resolution: {integrity: sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/environment': 29.3.1 - '@jest/fake-timers': 29.3.1 + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 '@jest/globals': 29.3.1 '@jest/source-map': 29.2.0 - '@jest/test-result': 29.3.1 + '@jest/test-result': 29.7.0 '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.18.4 chalk: 4.1.2 cjs-module-lexer: 1.2.2 @@ -13325,12 +13313,42 @@ packages: glob: 7.2.3 graceful-fs: 4.2.11 jest-haste-map: 29.3.1 - jest-message-util: 29.3.1 - jest-mock: 29.3.1 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 jest-regex-util: 29.2.0 jest-resolve: 29.3.1 - jest-snapshot: 29.3.1 - jest-util: 29.3.1 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + dev: true + + /jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.18.4 + chalk: 4.1.2 + cjs-module-lexer: 1.2.2 + collect-v8-coverage: 1.0.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: @@ -13343,74 +13361,71 @@ packages: diffable-html: 4.1.0 dev: true - /jest-snapshot@28.1.3: - resolution: {integrity: sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-snapshot@29.3.1: + resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.22.10 '@babel/generator': 7.23.0 + '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - '@jest/expect-utils': 28.1.3 - '@jest/transform': 28.1.3 - '@jest/types': 28.1.3 + '@babel/types': 7.23.4 + '@jest/expect-utils': 29.3.1 + '@jest/transform': 29.3.1 + '@jest/types': 29.6.3 '@types/babel__traverse': 7.20.3 '@types/prettier': 2.7.1 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) chalk: 4.1.2 - expect: 28.1.3 + expect: 29.3.1 graceful-fs: 4.2.11 - jest-diff: 28.1.3 - jest-get-type: 28.0.2 - jest-haste-map: 28.1.3 - jest-matcher-utils: 28.1.3 - jest-message-util: 28.1.3 - jest-util: 28.1.3 + jest-diff: 29.3.1 + jest-get-type: 29.2.0 + jest-haste-map: 29.3.1 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 natural-compare: 1.4.0 - pretty-format: 28.1.3 + pretty-format: 29.7.0 semver: 7.5.4 transitivePeerDependencies: - supports-color dev: true - /jest-snapshot@29.3.1: - resolution: {integrity: sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==} + /jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@babel/core': 7.22.10 '@babel/generator': 7.23.0 '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.22.10) '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.22.10) - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - '@jest/expect-utils': 29.3.1 - '@jest/transform': 29.3.1 - '@jest/types': 29.3.1 - '@types/babel__traverse': 7.20.3 - '@types/prettier': 2.7.1 + '@babel/types': 7.23.4 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 babel-preset-current-node-syntax: 1.0.1(@babel/core@7.22.10) chalk: 4.1.2 - expect: 29.3.1 + expect: 29.7.0 graceful-fs: 4.2.11 - jest-diff: 29.3.1 - jest-get-type: 29.2.0 - jest-haste-map: 29.3.1 - jest-matcher-utils: 29.3.1 - jest-message-util: 29.3.1 - jest-util: 29.3.1 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 natural-compare: 1.4.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 semver: 7.5.4 transitivePeerDependencies: - supports-color dev: true - /jest-util@28.1.3: - resolution: {integrity: sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-util@29.3.1: + resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 28.1.3 + '@jest/types': 29.3.1 '@types/node': 18.18.4 chalk: 4.1.2 ci-info: 3.5.0 @@ -13418,11 +13433,11 @@ packages: picomatch: 2.3.1 dev: true - /jest-util@29.3.1: - resolution: {integrity: sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==} + /jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 '@types/node': 18.18.4 chalk: 4.1.2 ci-info: 3.5.0 @@ -13430,31 +13445,31 @@ packages: picomatch: 2.3.1 dev: true - /jest-validate@28.1.3: - resolution: {integrity: sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-validate@29.3.1: + resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 28.1.3 + '@jest/types': 29.6.3 camelcase: 6.3.0 chalk: 4.1.2 - jest-get-type: 28.0.2 + jest-get-type: 29.2.0 leven: 3.1.0 - pretty-format: 28.1.3 + pretty-format: 29.7.0 dev: true - /jest-validate@29.3.1: - resolution: {integrity: sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==} + /jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/types': 29.3.1 + '@jest/types': 29.6.3 camelcase: 6.3.0 chalk: 4.1.2 - jest-get-type: 29.2.0 + jest-get-type: 29.6.3 leven: 3.1.0 - pretty-format: 29.3.1 + pretty-format: 29.7.0 dev: true - /jest-watch-typeahead@2.2.2(jest@28.1.3): + /jest-watch-typeahead@2.2.2(jest@29.7.0): resolution: {integrity: sha512-+QgOFW4o5Xlgd6jGS5X37i08tuuXNW8X0CV9WNFi+3n8ExCIP+E1melYhvYLjv5fE6D0yyzk74vsSO8I6GqtvQ==} engines: {node: ^14.17.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -13462,39 +13477,39 @@ packages: dependencies: ansi-escapes: 6.0.0 chalk: 5.2.0 - jest: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1) - jest-regex-util: 29.2.0 - jest-watcher: 29.3.1 + jest: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) + jest-regex-util: 29.6.3 + jest-watcher: 29.7.0 slash: 5.0.0 string-length: 5.0.1 strip-ansi: 7.0.1 dev: true - /jest-watcher@28.1.3: - resolution: {integrity: sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-watcher@29.3.1: + resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 28.1.3 - '@jest/types': 28.1.3 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.18.4 ansi-escapes: 4.3.2 chalk: 4.1.2 - emittery: 0.10.2 - jest-util: 28.1.3 + emittery: 0.13.1 + jest-util: 29.7.0 string-length: 4.0.2 dev: true - /jest-watcher@29.3.1: - resolution: {integrity: sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==} + /jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/test-result': 29.3.1 - '@jest/types': 29.3.1 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 '@types/node': 18.18.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 - jest-util: 29.3.1 + jest-util: 29.7.0 string-length: 4.0.2 dev: true @@ -13507,28 +13522,29 @@ packages: supports-color: 8.1.1 dev: true - /jest-worker@28.1.3: - resolution: {integrity: sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest-worker@29.3.1: + resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@types/node': 18.18.4 + jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest-worker@29.3.1: - resolution: {integrity: sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==} + /jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: '@types/node': 18.18.4 - jest-util: 29.3.1 + jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 dev: true - /jest@28.1.3(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /jest@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -13536,18 +13552,19 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 28.1.3(ts-node@10.9.1) - '@jest/types': 28.1.3 + '@jest/core': 29.3.1(ts-node@10.9.1) + '@jest/types': 29.3.1 import-local: 3.1.0 - jest-cli: 28.1.3(@types/node@18.11.9)(ts-node@10.9.1) + jest-cli: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1) transitivePeerDependencies: - '@types/node' + - babel-plugin-macros - supports-color - ts-node dev: true - /jest@29.3.1(@types/node@18.11.9)(ts-node@10.9.1): - resolution: {integrity: sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==} + /jest@29.7.0(@types/node@18.11.9)(ts-node@10.9.1): + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true peerDependencies: @@ -13556,12 +13573,13 @@ packages: node-notifier: optional: true dependencies: - '@jest/core': 29.3.1(ts-node@10.9.1) - '@jest/types': 29.3.1 + '@jest/core': 29.7.0(ts-node@10.9.1) + '@jest/types': 29.6.3 import-local: 3.1.0 - jest-cli: 29.3.1(@types/node@18.11.9)(ts-node@10.9.1) + jest-cli: 29.7.0(@types/node@18.11.9)(ts-node@10.9.1) transitivePeerDependencies: - '@types/node' + - babel-plugin-macros - supports-color - ts-node dev: true @@ -13610,7 +13628,7 @@ packages: '@babel/preset-env': ^7.1.6 dependencies: '@babel/core': 7.22.10 - '@babel/parser': 7.23.0 + '@babel/parser': 7.23.4 '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.22.10) '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.22.10) '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.22.10) @@ -15787,8 +15805,8 @@ packages: resolution: {integrity: sha512-tlkBdypJuvK/s00n4EiQjwYVfuuZv6vt8BF3g1ooIQa2Gz9Vz80p8q3qsPLZ0V5ErGRy6i3Q4fWC9TDzR7GNRQ==} dev: false - /posthog-js@1.92.0: - resolution: {integrity: sha512-87bZ/qwBbIqvkIV4YYn65oIPEsRcWihA3jX7WV33LvZWaU1InlE6cwj95SleIVLiND4Ofm+cKXZeWwcRnrXkKA==} + /posthog-js@1.92.1: + resolution: {integrity: sha512-xtuTfM/acfDauiEfIdKF6d911KUZQ7RLii2COAYEoPWr3cVUFoNUoRQz9QJvgDlV2j22Zwl+mnXacUeua+Yi1A==} dependencies: fflate: 0.4.8 dev: false @@ -15834,21 +15852,20 @@ packages: ansi-styles: 5.2.0 react-is: 17.0.2 - /pretty-format@28.1.3: - resolution: {integrity: sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==} - engines: {node: ^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0} + /pretty-format@29.3.1: + resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 28.1.3 - ansi-regex: 5.0.1 + '@jest/schemas': 29.0.0 ansi-styles: 5.2.0 react-is: 18.2.0 dev: true - /pretty-format@29.3.1: - resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} dependencies: - '@jest/schemas': 29.0.0 + '@jest/schemas': 29.6.3 ansi-styles: 5.2.0 react-is: 18.2.0 dev: true @@ -16139,6 +16156,10 @@ packages: resolution: {integrity: sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==} dev: false + /pure-rand@6.0.4: + resolution: {integrity: sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==} + dev: true + /q@1.5.1: resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} engines: {node: '>=0.6.0', teleport: '>=0.2.0'} @@ -16838,8 +16859,8 @@ packages: react: 18.2.0 dev: true - /react-intersection-observer@9.4.3(react@18.2.0): - resolution: {integrity: sha512-WNRqMQvKpupr6MzecAQI0Pj0+JQong307knLP4g/nBex7kYfIaZsPpXaIhKHR+oV8z+goUbH9e10j6lGRnTzlQ==} + /react-intersection-observer@9.5.3(react@18.2.0): + resolution: {integrity: sha512-NJzagSdUPS5rPhaLsHXYeJbsvdpbJwL6yCHtMk91hc0ufQ2BnXis+0QQ9NBh6n9n+Q3OyjR6OQLShYbaNBkThQ==} peerDependencies: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: @@ -17391,6 +17412,11 @@ packages: engines: {node: '>=10'} dev: true + /resolve.exports@2.0.2: + resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + engines: {node: '>=10'} + dev: true + /resolve@1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -18409,14 +18435,6 @@ packages: dependencies: has-flag: 4.0.0 - /supports-hyperlinks@2.3.0: - resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - dev: true - /supports-hyperlinks@3.0.0: resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} engines: {node: '>=14.18'} @@ -18561,14 +18579,6 @@ packages: unique-string: 2.0.0 dev: true - /terminal-link@2.1.1: - resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==} - engines: {node: '>=8'} - dependencies: - ansi-escapes: 4.3.2 - supports-hyperlinks: 2.3.0 - dev: true - /terser-webpack-plugin@5.3.9(@swc/core@1.3.93)(esbuild@0.14.54)(webpack@5.88.2): resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==} engines: {node: '>= 10.13.0'} @@ -19319,7 +19329,7 @@ packages: resolution: {integrity: sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==} engines: {node: '>=10.12.0'} dependencies: - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/trace-mapping': 0.3.20 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 dev: true diff --git a/posthog/api/cohort.py b/posthog/api/cohort.py index c525be8d263be..90324a48bfda0 100644 --- a/posthog/api/cohort.py +++ b/posthog/api/cohort.py @@ -1,5 +1,16 @@ import csv import json + +from django.db import DatabaseError +import structlog + +from posthog.models.feature_flag.flag_matching import ( + FeatureFlagMatcher, + FlagsMatcherCache, + get_feature_flag_hash_key_overrides, +) +from posthog.models.person.person import PersonDistinctId +from posthog.models.property.property import Property from posthog.queries.insight import insight_sync_execute import posthoganalytics from posthog.metrics import LABEL_TEAM_ID @@ -8,7 +19,7 @@ from typing import Any, Dict, cast from django.conf import settings -from django.db.models import QuerySet +from django.db.models import QuerySet, Prefetch, prefetch_related_objects, OuterRef, Subquery from django.db.models.expressions import F from django.utils import timezone from rest_framework import serializers, viewsets @@ -67,6 +78,7 @@ from posthog.queries.util import get_earliest_timestamp from posthog.tasks.calculate_cohort import ( calculate_cohort_from_list, + insert_cohort_from_feature_flag, insert_cohort_from_insight_filter, update_cohort, ) @@ -80,6 +92,8 @@ labelnames=[LABEL_TEAM_ID], ) +logger = structlog.get_logger(__name__) + class CohortSerializer(serializers.ModelSerializer): created_by = UserBasicSerializer(read_only=True) @@ -116,6 +130,8 @@ def _handle_static(self, cohort: Cohort, context: Dict) -> None: request = self.context["request"] if request.FILES.get("csv"): self._calculate_static_by_csv(request.FILES["csv"], cohort) + elif context.get("from_feature_flag_key"): + insert_cohort_from_feature_flag.delay(cohort.pk, context["from_feature_flag_key"], self.context["team_id"]) else: filter_data = request.GET.dict() existing_cohort_id = context.get("from_cohort_id") @@ -539,3 +555,142 @@ def insert_actors_into_cohort_by_query(cohort: Cohort, query: str, params: Dict[ cohort.errors_calculating = F("errors_calculating") + 1 cohort.save(update_fields=["errors_calculating", "is_calculating"]) capture_exception(err) + + +def get_cohort_actors_for_feature_flag(cohort_id: int, flag: str, team_id: int, batchsize: int = 1_000): + # :TODO: Find a way to incorporate this into the same code path as feature flag evaluation + try: + feature_flag = FeatureFlag.objects.get(team_id=team_id, key=flag) + except FeatureFlag.DoesNotExist: + return [] + + if not feature_flag.active or feature_flag.deleted or feature_flag.aggregation_group_type_index is not None: + return [] + + cohort = Cohort.objects.get(pk=cohort_id) + matcher_cache = FlagsMatcherCache(team_id) + uuids_to_add_to_cohort = [] + cohorts_cache = {} + + if feature_flag.uses_cohorts: + # TODO: Consider disabling flags with cohorts for creating static cohorts + # because this is currently a lot more inefficient for flag matching, + # as we're required to go to the database for each person. + cohorts_cache = {cohort.pk: cohort for cohort in Cohort.objects.filter(team_id=team_id, deleted=False)} + + default_person_properties = {} + for condition in feature_flag.conditions: + property_list = Filter(data=condition).property_groups.flat + for property in property_list: + default_person_properties.update(get_default_person_property(property, cohorts_cache)) + + try: + # QuerySet.Iterator() doesn't work with pgbouncer, it will load everything into memory and then stream + # which doesn't work for us, so need a manual chunking here. + # Because of this pgbouncer transaction pooling mode, we can't use server-side cursors. + queryset = Person.objects.filter(team_id=team_id).order_by("id") + # get batchsize number of people at a time + start = 0 + batch_of_persons = queryset[start : start + batchsize] + while batch_of_persons: + # TODO: Check if this subquery bulk fetch limiting is better than just doing a join for all distinct ids + # OR, if row by row getting single distinct id is better + # distinct_id = PersonDistinctId.objects.filter(person=person, team_id=team_id).values_list( + # "distinct_id", flat=True + # )[0] + distinct_id_subquery = Subquery( + PersonDistinctId.objects.filter(person_id=OuterRef("person_id")).values_list("id", flat=True)[:3] + ) + prefetch_related_objects( + batch_of_persons, + Prefetch( + "persondistinctid_set", + to_attr="distinct_ids_cache", + queryset=PersonDistinctId.objects.filter(id__in=distinct_id_subquery), + ), + ) + + all_persons = list(batch_of_persons) + if len(all_persons) == 0: + break + + for person in all_persons: + # ignore almost-deleted persons / persons with no distinct ids + if len(person.distinct_ids) == 0: + continue + + distinct_id = person.distinct_ids[0] + person_overrides = {} + if feature_flag.ensure_experience_continuity: + # :TRICKY: This is inefficient because it tries to get the hashkey overrides one by one. + # But reusing functions is better for maintainability. Revisit optimising if this becomes a bottleneck. + person_overrides = get_feature_flag_hash_key_overrides( + team_id, [distinct_id], person_id_to_distinct_id_mapping={person.id: distinct_id} + ) + + try: + match = FeatureFlagMatcher( + [feature_flag], + distinct_id, + groups={}, + cache=matcher_cache, + hash_key_overrides=person_overrides, + property_value_overrides={**default_person_properties, **person.properties}, + group_property_value_overrides={}, + cohorts_cache=cohorts_cache, + ).get_match(feature_flag) + if match.match: + uuids_to_add_to_cohort.append(str(person.uuid)) + except (DatabaseError, ValueError, ValidationError): + logger.exception( + "Error evaluating feature flag for person", person_uuid=str(person.uuid), team_id=team_id + ) + except Exception as err: + # matching errors are not fatal, so we just log them and move on. + # Capturing in sentry for now just in case there are some unexpected errors + # we did not account for. + capture_exception(err) + + if len(uuids_to_add_to_cohort) >= batchsize - 1: + cohort.insert_users_list_by_uuid( + uuids_to_add_to_cohort, insert_in_clickhouse=True, batchsize=batchsize + ) + uuids_to_add_to_cohort = [] + + start += batchsize + batch_of_persons = queryset[start : start + batchsize] + + if len(uuids_to_add_to_cohort) > 0: + cohort.insert_users_list_by_uuid(uuids_to_add_to_cohort, insert_in_clickhouse=True, batchsize=batchsize) + + except Exception as err: + if settings.DEBUG or settings.TEST: + raise err + capture_exception(err) + + +def get_default_person_property(prop: Property, cohorts_cache: Dict[int, Cohort]): + default_person_properties = {} + + if prop.operator not in ("is_set", "is_not_set") and prop.type == "person": + default_person_properties[prop.key] = "" + elif prop.type == "cohort" and not isinstance(prop.value, list): + try: + parsed_cohort_id = int(prop.value) + except (ValueError, TypeError): + return None + cohort = cohorts_cache.get(parsed_cohort_id) + if cohort: + return get_default_person_properties_for_cohort(cohort, cohorts_cache) + return default_person_properties + + +def get_default_person_properties_for_cohort(cohort: Cohort, cohorts_cache: Dict[int, Cohort]) -> Dict[str, str]: + """ + Returns a dictionary of default person properties to use when evaluating a feature flag + """ + default_person_properties = {} + for property in cohort.properties.flat: + default_person_properties.update(get_default_person_property(property, cohorts_cache)) + + return default_person_properties diff --git a/posthog/api/feature_flag.py b/posthog/api/feature_flag.py index f513e9e74b6a4..92add84a0bcab 100644 --- a/posthog/api/feature_flag.py +++ b/posthog/api/feature_flag.py @@ -1,5 +1,6 @@ import json from typing import Any, Dict, List, Optional, cast +from datetime import datetime from django.db.models import QuerySet, Q, deletion from django.conf import settings @@ -16,6 +17,7 @@ from rest_framework.request import Request from rest_framework.response import Response from sentry_sdk import capture_exception +from posthog.api.cohort import CohortSerializer from posthog.api.forbid_destroy_model import ForbidDestroyModel from posthog.api.routing import StructuredViewSetMixin @@ -503,11 +505,11 @@ def local_evaluation(self, request: request.Request, **kwargs): should_send_cohorts = "send_cohorts" in request.GET cohorts = {} - seen_cohorts_cache: Dict[str, Cohort] = {} + seen_cohorts_cache: Dict[int, Cohort] = {} if should_send_cohorts: seen_cohorts_cache = { - str(cohort.pk): cohort + cohort.pk: cohort for cohort in Cohort.objects.using(DATABASE_FOR_LOCAL_EVALUATION).filter( team_id=self.team_id, deleted=False ) @@ -547,12 +549,11 @@ def local_evaluation(self, request: request.Request, **kwargs): ): # don't duplicate queries for already added cohorts if id not in cohorts: - parsed_cohort_id = str(id) - if parsed_cohort_id in seen_cohorts_cache: - cohort = seen_cohorts_cache[parsed_cohort_id] + if id in seen_cohorts_cache: + cohort = seen_cohorts_cache[id] else: cohort = Cohort.objects.using(DATABASE_FOR_LOCAL_EVALUATION).get(id=id) - seen_cohorts_cache[parsed_cohort_id] = cohort + seen_cohorts_cache[id] = cohort if not cohort.is_static: cohorts[cohort.pk] = cohort.properties.to_dict() @@ -626,6 +627,28 @@ def user_blast_radius(self, request: request.Request, **kwargs): } ) + @action(methods=["POST"], detail=True) + def create_static_cohort_for_flag(self, request: request.Request, **kwargs): + feature_flag = self.get_object() + feature_flag_key = feature_flag.key + cohort_serializer = CohortSerializer( + data={ + "is_static": True, + "key": feature_flag_key, + "name": f'Users with feature flag {feature_flag_key} enabled at {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}', + }, + context={ + "request": request, + "team": self.team, + "team_id": self.team_id, + "from_feature_flag_key": feature_flag_key, + }, + ) + + cohort_serializer.is_valid(raise_exception=True) + cohort_serializer.save() + return Response({"cohort": cohort_serializer.data}, status=201) + @action(methods=["GET"], url_path="activity", detail=False) def all_activity(self, request: request.Request, **kwargs): limit = int(request.query_params.get("limit", "10")) diff --git a/posthog/api/organization_feature_flag.py b/posthog/api/organization_feature_flag.py index d149de721dccb..44648bd2cd0f2 100644 --- a/posthog/api/organization_feature_flag.py +++ b/posthog/api/organization_feature_flag.py @@ -12,6 +12,7 @@ from posthog.api.routing import StructuredViewSetMixin from posthog.api.feature_flag import FeatureFlagSerializer from posthog.api.feature_flag import CanEditFeatureFlag +from posthog.api.shared import UserBasicSerializer from posthog.models import FeatureFlag, Team from posthog.models.cohort import Cohort from posthog.models.filters.filter import Filter @@ -44,15 +45,10 @@ def retrieve(self, request, *args, **kwargs): { "flag_id": flag.id, "team_id": flag.team_id, - "created_by": { - "id": flag.created_by.id, - "uuid": flag.created_by.uuid, - "distinct_id": flag.created_by.distinct_id, - "first_name": flag.created_by.first_name, - "email": flag.created_by.email, - "is_email_verified": flag.created_by.is_email_verified, - }, - "filters": flag.filters, + "created_by": UserBasicSerializer(flag.created_by).data + if hasattr(flag, "created_by") and flag.created_by + else None, + "filters": flag.get_filters(), "created_at": flag.created_at, "active": flag.active, } @@ -101,7 +97,7 @@ def copy_flags(self, request, *args, **kwargs): continue # get all linked cohorts, sorted by creation order - seen_cohorts_cache: Dict[str, Cohort] = {} + seen_cohorts_cache: Dict[int, Cohort] = {} sorted_cohort_ids = flag_to_copy.get_cohort_ids( seen_cohorts_cache=seen_cohorts_cache, sort_by_topological_order=True ) @@ -111,7 +107,7 @@ def copy_flags(self, request, *args, **kwargs): # create cohorts in the destination project if len(sorted_cohort_ids): for cohort_id in sorted_cohort_ids: - original_cohort = seen_cohorts_cache[str(cohort_id)] + original_cohort = seen_cohorts_cache[cohort_id] # search in destination project by name destination_cohort = Cohort.objects.filter( @@ -125,10 +121,13 @@ def copy_flags(self, request, *args, **kwargs): ).property_groups for prop in prop_group.flat: - if prop.type == "cohort": - original_child_cohort_id = prop.value - original_child_cohort = seen_cohorts_cache[str(original_child_cohort_id)] - prop.value = name_to_dest_cohort_id[original_child_cohort.name] + if prop.type == "cohort" and not isinstance(prop.value, list): + try: + original_child_cohort_id = int(prop.value) + original_child_cohort = seen_cohorts_cache[original_child_cohort_id] + prop.value = name_to_dest_cohort_id[original_child_cohort.name] + except (ValueError, TypeError): + continue destination_cohort_serializer = CohortSerializer( data={ @@ -155,14 +154,17 @@ def copy_flags(self, request, *args, **kwargs): props = group.get("properties", []) for prop in props: if isinstance(prop, dict) and prop.get("type") == "cohort": - original_cohort_id = prop["value"] - cohort_name = (seen_cohorts_cache[str(original_cohort_id)]).name - prop["value"] = name_to_dest_cohort_id[cohort_name] + try: + original_cohort_id = int(prop["value"]) + cohort_name = (seen_cohorts_cache[original_cohort_id]).name + prop["value"] = name_to_dest_cohort_id[cohort_name] + except (ValueError, TypeError): + continue flag_data = { "key": flag_to_copy.key, "name": flag_to_copy.name, - "filters": flag_to_copy.filters, + "filters": flag_to_copy.get_filters(), "active": flag_to_copy.active, "rollout_percentage": flag_to_copy.rollout_percentage, "ensure_experience_continuity": flag_to_copy.ensure_experience_continuity, diff --git a/posthog/api/search.py b/posthog/api/search.py index a48f716f902f7..cbdd898949fd1 100644 --- a/posthog/api/search.py +++ b/posthog/api/search.py @@ -21,8 +21,8 @@ ENTITY_MAP = { "insight": { "klass": Insight, - "search_fields": {"name": "A", "derived_name": "B", "description": "C"}, - "extra_fields": ["name", "derived_name", "description"], + "search_fields": {"name": "A", "description": "C"}, + "extra_fields": ["name", "description", "filters", "query"], }, "dashboard": { "klass": Dashboard, diff --git a/posthog/api/test/__snapshots__/test_feature_flag.ambr b/posthog/api/test/__snapshots__/test_feature_flag.ambr index 6fc6c63c17f20..ffe583b425eac 100644 --- a/posthog/api/test/__snapshots__/test_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_feature_flag.ambr @@ -334,6 +334,2196 @@ HAVING max(is_deleted) = 0 SETTINGS optimize_aggregation_in_order = 1) ' --- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.1 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.10 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 2 + OFFSET 4 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.11 + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.12 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.13 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 10 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.14 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 1) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.15 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 10 + OFFSET 10 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.16 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.17 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.2 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 2 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.3 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.4 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.5 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.6 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.7 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 2 + OFFSET 2 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.8 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_iterator.9 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature-new' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.1 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.10 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.11 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.2 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE (NOT "posthog_cohort"."deleted" + AND "posthog_cohort"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.3 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.4 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.5 + ' + SELECT ("posthog_person"."id" IS NULL + OR "posthog_person"."id" IS NULL + OR EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U0 + WHERE (U0."cohort_id" = 2 + AND U0."cohort_id" = 2 + AND U0."person_id" = "posthog_person"."id") + LIMIT 1) + OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0" + FROM "posthog_person" + INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id") + WHERE ("posthog_persondistinctid"."distinct_id" = 'person1' + AND "posthog_persondistinctid"."team_id" = 2 + AND "posthog_person"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.6 + ' + SELECT ("posthog_person"."id" IS NOT NULL + OR "posthog_person"."id" IS NULL + OR EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U0 + WHERE (U0."cohort_id" = 2 + AND U0."cohort_id" = 2 + AND U0."person_id" = "posthog_person"."id") + LIMIT 1) + OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0" + FROM "posthog_person" + INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id") + WHERE ("posthog_persondistinctid"."distinct_id" = 'person2' + AND "posthog_persondistinctid"."team_id" = 2 + AND "posthog_person"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.7 + ' + SELECT ("posthog_person"."id" IS NULL + OR "posthog_person"."id" IS NOT NULL + OR EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U0 + WHERE (U0."cohort_id" = 2 + AND U0."cohort_id" = 2 + AND U0."person_id" = "posthog_person"."id") + LIMIT 1) + OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0" + FROM "posthog_person" + INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id") + WHERE ("posthog_persondistinctid"."distinct_id" = 'person3' + AND "posthog_persondistinctid"."team_id" = 2 + AND "posthog_person"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.8 + ' + SELECT ("posthog_person"."id" IS NULL + OR "posthog_person"."id" IS NULL + OR EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U0 + WHERE (U0."cohort_id" = 2 + AND U0."cohort_id" = 2 + AND U0."person_id" = "posthog_person"."id") + LIMIT 1) + OR "posthog_person"."id" IS NULL) AS "flag_X_condition_0" + FROM "posthog_person" + INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id") + WHERE ("posthog_persondistinctid"."distinct_id" = 'person4' + AND "posthog_persondistinctid"."team_id" = 2 + AND "posthog_person"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too.9 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + OFFSET 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.1 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.10 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.11 + ' + SELECT ("posthog_person"."properties" -> 'key') IS NOT NULL AS "flag_X_condition_0" + FROM "posthog_person" + INNER JOIN "posthog_persondistinctid" ON ("posthog_person"."id" = "posthog_persondistinctid"."person_id") + WHERE ("posthog_persondistinctid"."distinct_id" = 'person3' + AND "posthog_persondistinctid"."team_id" = 2 + AND "posthog_person"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.12 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + OFFSET 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.13 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.14 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.2 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.3 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.4 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + OFFSET 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.5 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.6 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.7 + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature-new' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.8 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_default_person_properties_adjustment.9 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_deleted_flag + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.1 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.10 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.11 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 21 + OFFSET 5000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.12 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 5000 + OFFSET 5000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.13 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.14 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.2 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.3 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.4 + ' + SELECT "posthog_featureflaghashkeyoverride"."feature_flag_key", + "posthog_featureflaghashkeyoverride"."hash_key", + "posthog_featureflaghashkeyoverride"."person_id" + FROM "posthog_featureflaghashkeyoverride" + WHERE ("posthog_featureflaghashkeyoverride"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */) + AND "posthog_featureflaghashkeyoverride"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.5 + ' + SELECT "posthog_featureflaghashkeyoverride"."feature_flag_key", + "posthog_featureflaghashkeyoverride"."hash_key", + "posthog_featureflaghashkeyoverride"."person_id" + FROM "posthog_featureflaghashkeyoverride" + WHERE ("posthog_featureflaghashkeyoverride"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */) + AND "posthog_featureflaghashkeyoverride"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.6 + ' + SELECT "posthog_featureflaghashkeyoverride"."feature_flag_key", + "posthog_featureflaghashkeyoverride"."hash_key", + "posthog_featureflaghashkeyoverride"."person_id" + FROM "posthog_featureflaghashkeyoverride" + WHERE ("posthog_featureflaghashkeyoverride"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */) + AND "posthog_featureflaghashkeyoverride"."team_id" = 2) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.7 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + OFFSET 1000 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.8 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_experience_continuity_flag.9 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature3' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.1 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.2 + ' + DECLARE "_django_curs_X" NO SCROLL + CURSOR WITHOUT HOLD + FOR + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.3 + ' + SELECT "posthog_persondistinctid"."distinct_id" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."person_id" = 2 + AND "posthog_persondistinctid"."team_id" = 2) + LIMIT 1 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_group_flag.4 + ' + SELECT "posthog_grouptypemapping"."id", + "posthog_grouptypemapping"."team_id", + "posthog_grouptypemapping"."group_type", + "posthog_grouptypemapping"."group_type_index", + "posthog_grouptypemapping"."name_singular", + "posthog_grouptypemapping"."name_plural" + FROM "posthog_grouptypemapping" + WHERE "posthog_grouptypemapping"."team_id" = 2 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_inactive_flag + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_invalid_flags + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestCohortGenerationForFeatureFlag.test_creating_static_cohort_with_non_existing_flag + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature2' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort + ' + SELECT "posthog_user"."id", + "posthog_user"."password", + "posthog_user"."last_login", + "posthog_user"."first_name", + "posthog_user"."last_name", + "posthog_user"."is_staff", + "posthog_user"."is_active", + "posthog_user"."date_joined", + "posthog_user"."uuid", + "posthog_user"."current_organization_id", + "posthog_user"."current_team_id", + "posthog_user"."email", + "posthog_user"."pending_email", + "posthog_user"."temporary_token", + "posthog_user"."distinct_id", + "posthog_user"."is_email_verified", + "posthog_user"."has_seen_product_intro_for", + "posthog_user"."email_opt_in", + "posthog_user"."partial_notification_settings", + "posthog_user"."anonymize_data", + "posthog_user"."toolbar_mode", + "posthog_user"."events_column_config" + FROM "posthog_user" + WHERE "posthog_user"."id" = 2 + LIMIT 21 /**/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.1 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.10 + ' + SELECT "posthog_persondistinctid"."id", + "posthog_persondistinctid"."team_id", + "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id", + "posthog_persondistinctid"."version" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."id" IN + (SELECT U0."id" + FROM "posthog_persondistinctid" U0 + WHERE U0."person_id" = "posthog_persondistinctid"."person_id" + LIMIT 3) + AND "posthog_persondistinctid"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */)) /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.11 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 + OFFSET 1000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.12 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.13 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.14 + ' + /* user_id:189 celery:posthog.tasks.calculate_cohort.insert_cohort_from_feature_flag */ + SELECT count(DISTINCT person_id) + FROM person_static_cohort + WHERE team_id = 2 + AND cohort_id = 2 + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.15 + ' + /* user_id:0 request:_snapshot_ */ + SELECT id + FROM person + INNER JOIN + (SELECT person_id + FROM person_static_cohort + WHERE team_id = 2 + AND cohort_id = 2 + GROUP BY person_id, + cohort_id, + team_id) cohort_persons ON cohort_persons.person_id = person.id + WHERE team_id = 2 + GROUP BY id + HAVING max(is_deleted) = 0 + ORDER BY argMax(person.created_at, version) DESC, id DESC + LIMIT 100 SETTINGS optimize_aggregation_in_order = 1 + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.16 + ' + /* user_id:0 request:_snapshot_ */ + SELECT id + FROM person + INNER JOIN + (SELECT person_id + FROM person_static_cohort + WHERE team_id = 2 + AND cohort_id = 2 + GROUP BY person_id, + cohort_id, + team_id) cohort_persons ON cohort_persons.person_id = person.id + WHERE team_id = 2 + GROUP BY id + HAVING max(is_deleted) = 0 + ORDER BY argMax(person.created_at, version) DESC, id DESC + LIMIT 100 SETTINGS optimize_aggregation_in_order = 1 + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.17 + ' + SELECT "posthog_persondistinctid"."person_id", + "posthog_persondistinctid"."distinct_id" + FROM "posthog_persondistinctid" + WHERE ("posthog_persondistinctid"."distinct_id" IN ('person3') + AND "posthog_persondistinctid"."team_id" = 2) /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.18 + ' + SELECT "posthog_featureflaghashkeyoverride"."feature_flag_key", + "posthog_featureflaghashkeyoverride"."hash_key", + "posthog_featureflaghashkeyoverride"."person_id" + FROM "posthog_featureflaghashkeyoverride" + WHERE ("posthog_featureflaghashkeyoverride"."person_id" IN (1, + 2, + 3, + 4, + 5 /* ... */) + AND "posthog_featureflaghashkeyoverride"."team_id" = 2) /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.19 + ' + SELECT "posthog_person"."uuid" + FROM "posthog_person" + WHERE ("posthog_person"."team_id" = 2 + AND "posthog_person"."uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, + '00000000-0000-0000-0000-000000000001'::uuid /* ... */) + AND NOT (EXISTS + (SELECT (1) AS "a" + FROM "posthog_cohortpeople" U1 + WHERE (U1."cohort_id" = 2 + AND U1."person_id" = "posthog_person"."id") + LIMIT 1))) /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.2 + ' + SELECT "posthog_organizationmembership"."id", + "posthog_organizationmembership"."organization_id", + "posthog_organizationmembership"."user_id", + "posthog_organizationmembership"."level", + "posthog_organizationmembership"."joined_at", + "posthog_organizationmembership"."updated_at", + "posthog_organization"."id", + "posthog_organization"."name", + "posthog_organization"."slug", + "posthog_organization"."created_at", + "posthog_organization"."updated_at", + "posthog_organization"."plugins_access_level", + "posthog_organization"."for_internal_metrics", + "posthog_organization"."is_member_join_email_enabled", + "posthog_organization"."enforce_2fa", + "posthog_organization"."customer_id", + "posthog_organization"."available_product_features", + "posthog_organization"."usage", + "posthog_organization"."never_drop_data", + "posthog_organization"."setup_section_2_completed", + "posthog_organization"."personalization", + "posthog_organization"."domain_whitelist", + "posthog_organization"."available_features" + FROM "posthog_organizationmembership" + INNER JOIN "posthog_organization" ON ("posthog_organizationmembership"."organization_id" = "posthog_organization"."id") + WHERE "posthog_organizationmembership"."user_id" = 2 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.20 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.3 + ' + SELECT "posthog_instancesetting"."id", + "posthog_instancesetting"."key", + "posthog_instancesetting"."raw_value" + FROM "posthog_instancesetting" + WHERE "posthog_instancesetting"."key" = 'constance:posthog:RATE_LIMIT_ENABLED' + ORDER BY "posthog_instancesetting"."id" ASC + LIMIT 1 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.4 + ' + SELECT "posthog_organization"."id", + "posthog_organization"."name", + "posthog_organization"."slug", + "posthog_organization"."created_at", + "posthog_organization"."updated_at", + "posthog_organization"."plugins_access_level", + "posthog_organization"."for_internal_metrics", + "posthog_organization"."is_member_join_email_enabled", + "posthog_organization"."enforce_2fa", + "posthog_organization"."customer_id", + "posthog_organization"."available_product_features", + "posthog_organization"."usage", + "posthog_organization"."never_drop_data", + "posthog_organization"."setup_section_2_completed", + "posthog_organization"."personalization", + "posthog_organization"."domain_whitelist", + "posthog_organization"."available_features" + FROM "posthog_organization" + WHERE "posthog_organization"."id" = '00000000-0000-0000-0000-000000000000'::uuid + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.5 + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics", + "posthog_user"."id", + "posthog_user"."password", + "posthog_user"."last_login", + "posthog_user"."first_name", + "posthog_user"."last_name", + "posthog_user"."is_staff", + "posthog_user"."is_active", + "posthog_user"."date_joined", + "posthog_user"."uuid", + "posthog_user"."current_organization_id", + "posthog_user"."current_team_id", + "posthog_user"."email", + "posthog_user"."pending_email", + "posthog_user"."temporary_token", + "posthog_user"."distinct_id", + "posthog_user"."is_email_verified", + "posthog_user"."requested_password_reset_at", + "posthog_user"."has_seen_product_intro_for", + "posthog_user"."email_opt_in", + "posthog_user"."partial_notification_settings", + "posthog_user"."anonymize_data", + "posthog_user"."toolbar_mode", + "posthog_user"."events_column_config" + FROM "posthog_featureflag" + LEFT OUTER JOIN "posthog_user" ON ("posthog_featureflag"."created_by_id" = "posthog_user"."id") + WHERE ("posthog_featureflag"."team_id" = 2 + AND "posthog_featureflag"."id" = 2) + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.6 + ' + SELECT "posthog_team"."id", + "posthog_team"."uuid", + "posthog_team"."organization_id", + "posthog_team"."api_token", + "posthog_team"."app_urls", + "posthog_team"."name", + "posthog_team"."slack_incoming_webhook", + "posthog_team"."created_at", + "posthog_team"."updated_at", + "posthog_team"."anonymize_ips", + "posthog_team"."completed_snippet_onboarding", + "posthog_team"."has_completed_onboarding_for", + "posthog_team"."ingested_event", + "posthog_team"."autocapture_opt_out", + "posthog_team"."autocapture_exceptions_opt_in", + "posthog_team"."autocapture_exceptions_errors_to_ignore", + "posthog_team"."session_recording_opt_in", + "posthog_team"."session_recording_sample_rate", + "posthog_team"."session_recording_minimum_duration_milliseconds", + "posthog_team"."session_recording_linked_flag", + "posthog_team"."session_recording_network_payload_capture_config", + "posthog_team"."capture_console_log_opt_in", + "posthog_team"."capture_performance_opt_in", + "posthog_team"."surveys_opt_in", + "posthog_team"."session_recording_version", + "posthog_team"."signup_token", + "posthog_team"."is_demo", + "posthog_team"."access_control", + "posthog_team"."week_start_day", + "posthog_team"."inject_web_apps", + "posthog_team"."test_account_filters", + "posthog_team"."test_account_filters_default_checked", + "posthog_team"."path_cleaning_filters", + "posthog_team"."timezone", + "posthog_team"."data_attributes", + "posthog_team"."person_display_name_properties", + "posthog_team"."live_events_columns", + "posthog_team"."recording_domains", + "posthog_team"."primary_dashboard_id", + "posthog_team"."extra_settings", + "posthog_team"."correlation_config", + "posthog_team"."session_recording_retention_period_days", + "posthog_team"."plugins_opt_in", + "posthog_team"."opt_out_capture", + "posthog_team"."event_names", + "posthog_team"."event_names_with_usage", + "posthog_team"."event_properties", + "posthog_team"."event_properties_with_usage", + "posthog_team"."event_properties_numerical", + "posthog_team"."external_data_workspace_id", + "posthog_team"."external_data_workspace_last_synced_at" + FROM "posthog_team" + WHERE "posthog_team"."id" = 2 + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.7 + ' + SELECT "posthog_featureflag"."id", + "posthog_featureflag"."key", + "posthog_featureflag"."name", + "posthog_featureflag"."filters", + "posthog_featureflag"."rollout_percentage", + "posthog_featureflag"."team_id", + "posthog_featureflag"."created_by_id", + "posthog_featureflag"."created_at", + "posthog_featureflag"."deleted", + "posthog_featureflag"."active", + "posthog_featureflag"."rollback_conditions", + "posthog_featureflag"."performed_rollback", + "posthog_featureflag"."ensure_experience_continuity", + "posthog_featureflag"."usage_dashboard_id", + "posthog_featureflag"."has_enriched_analytics" + FROM "posthog_featureflag" + WHERE ("posthog_featureflag"."key" = 'some-feature' + AND "posthog_featureflag"."team_id" = 2) + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.8 + ' + SELECT "posthog_cohort"."id", + "posthog_cohort"."name", + "posthog_cohort"."description", + "posthog_cohort"."team_id", + "posthog_cohort"."deleted", + "posthog_cohort"."filters", + "posthog_cohort"."version", + "posthog_cohort"."pending_version", + "posthog_cohort"."count", + "posthog_cohort"."created_by_id", + "posthog_cohort"."created_at", + "posthog_cohort"."is_calculating", + "posthog_cohort"."last_calculation", + "posthog_cohort"."errors_calculating", + "posthog_cohort"."is_static", + "posthog_cohort"."groups" + FROM "posthog_cohort" + WHERE "posthog_cohort"."id" = 2 + LIMIT 21 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- +# name: TestFeatureFlag.test_creating_static_cohort.9 + ' + SELECT "posthog_person"."id", + "posthog_person"."created_at", + "posthog_person"."properties_last_updated_at", + "posthog_person"."properties_last_operation", + "posthog_person"."team_id", + "posthog_person"."properties", + "posthog_person"."is_user_id", + "posthog_person"."is_identified", + "posthog_person"."uuid", + "posthog_person"."version" + FROM "posthog_person" + WHERE "posthog_person"."team_id" = 2 + ORDER BY "posthog_person"."id" ASC + LIMIT 1000 /*controller='project_feature_flags-create-static-cohort-for-flag',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/feature_flags/%28%3FP%3Cpk%3E%5B%5E/.%5D%2B%29/create_static_cohort_for_flag/%3F%24'*/ + ' +--- # name: TestResiliency.test_feature_flags_v3_with_experience_continuity_working_slow_db ' WITH target_person_ids AS diff --git a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr index e2b852a604b20..9916b9afadbbf 100644 --- a/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr +++ b/posthog/api/test/__snapshots__/test_organization_feature_flag.ambr @@ -1440,8 +1440,8 @@ "posthog_experiment"."filters", "posthog_experiment"."parameters", "posthog_experiment"."secondary_metrics", - "posthog_experiment"."feature_flag_id", "posthog_experiment"."created_by_id", + "posthog_experiment"."feature_flag_id", "posthog_experiment"."start_date", "posthog_experiment"."end_date", "posthog_experiment"."created_at", diff --git a/posthog/api/test/batch_exports/test_delete.py b/posthog/api/test/batch_exports/test_delete.py index bbd1b2dda8abd..caef9bf3a2b09 100644 --- a/posthog/api/test/batch_exports/test_delete.py +++ b/posthog/api/test/batch_exports/test_delete.py @@ -241,3 +241,48 @@ def test_deletes_are_partitioned_by_team_id(client: HttpClient): # Make sure we can still get the export with the right user response = get_batch_export(client, team.pk, batch_export_id) assert response.status_code == status.HTTP_200_OK + + +@pytest.mark.django_db(transaction=True) +def test_delete_batch_export_even_without_underlying_schedule(client: HttpClient): + """Test deleting a BatchExport completes even if underlying Schedule was already deleted.""" + temporal = sync_connect() + + destination_data = { + "type": "S3", + "config": { + "bucket_name": "my-production-s3-bucket", + "region": "us-east-1", + "prefix": "posthog-events/", + "aws_access_key_id": "abc123", + "aws_secret_access_key": "secret", + }, + } + batch_export_data = { + "name": "my-production-s3-bucket-destination", + "destination": destination_data, + "interval": "hour", + } + + organization = create_organization("Test Org") + team = create_team(organization) + user = create_user("test@user.com", "Test User", organization) + client.force_login(user) + + with start_test_worker(temporal): + batch_export = create_batch_export_ok(client, team.pk, batch_export_data) + batch_export_id = batch_export["id"] + + handle = temporal.get_schedule_handle(batch_export_id) + async_to_sync(handle.delete)() + + with pytest.raises(RPCError): + describe_schedule(temporal, batch_export_id) + + delete_batch_export_ok(client, team.pk, batch_export_id) + + response = get_batch_export(client, team.pk, batch_export_id) + assert response.status_code == status.HTTP_404_NOT_FOUND + + with pytest.raises(RPCError): + describe_schedule(temporal, batch_export_id) diff --git a/posthog/api/test/batch_exports/test_pause.py b/posthog/api/test/batch_exports/test_pause.py index 458a60e595ae4..2c92a00af94ee 100644 --- a/posthog/api/test/batch_exports/test_pause.py +++ b/posthog/api/test/batch_exports/test_pause.py @@ -17,7 +17,8 @@ from posthog.api.test.test_organization import create_organization from posthog.api.test.test_team import create_team from posthog.api.test.test_user import create_user -from posthog.batch_exports.service import delete_schedule, describe_schedule +from posthog.batch_exports.service import batch_export_delete_schedule +from posthog.temporal.common.schedule import describe_schedule from posthog.temporal.common.client import sync_connect pytestmark = [ @@ -344,7 +345,7 @@ def test_pause_non_existent_batch_export(client: HttpClient): schedule_desc = describe_schedule(temporal, batch_export["id"]) assert schedule_desc.schedule.state.paused is False - resp = delete_schedule(temporal, batch_export["id"]) + resp = batch_export_delete_schedule(temporal, batch_export["id"]) batch_export_id = batch_export["id"] resp = pause_batch_export(client, team.pk, batch_export_id) diff --git a/posthog/api/test/test_feature_flag.py b/posthog/api/test/test_feature_flag.py index 0c6581a389561..31f46aabff9a0 100644 --- a/posthog/api/test/test_feature_flag.py +++ b/posthog/api/test/test_feature_flag.py @@ -12,6 +12,7 @@ from freezegun.api import freeze_time from rest_framework import status from posthog import redis +from posthog.api.cohort import get_cohort_actors_for_feature_flag from posthog.api.feature_flag import FeatureFlagSerializer from posthog.constants import AvailableFeature @@ -23,6 +24,7 @@ FeatureFlagDashboards, ) from posthog.models.dashboard import Dashboard +from posthog.models.feature_flag.feature_flag import FeatureFlagHashKeyOverride from posthog.models.group.util import create_group from posthog.models.organization import Organization from posthog.models.person import Person @@ -34,6 +36,7 @@ ClickhouseTestMixin, QueryMatchingTest, _create_person, + flush_persons_and_events, snapshot_clickhouse_queries, snapshot_postgres_queries_context, FuzzyInt, @@ -41,7 +44,7 @@ from posthog.test.db_context_capturing import capture_db_queries -class TestFeatureFlag(APIBaseTest): +class TestFeatureFlag(APIBaseTest, ClickhouseTestMixin): feature_flag: FeatureFlag = None # type: ignore maxDiff = None @@ -1171,6 +1174,34 @@ def test_getting_flags_is_not_nplus1(self) -> None: response = self.client.get(f"/api/projects/{self.team.id}/feature_flags") self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_getting_flags_with_no_creator(self) -> None: + FeatureFlag.objects.all().delete() + + self.client.post( + f"/api/projects/{self.team.id}/feature_flags/", + data={ + "name": f"flag", + "key": f"flag_0", + "filters": {"groups": [{"rollout_percentage": 5}]}, + }, + format="json", + ).json() + + FeatureFlag.objects.create( + created_by=None, + team=self.team, + key="flag_role_access", + name="Flag role access", + ) + + with self.assertNumQueries(FuzzyInt(11, 12)): + response = self.client.get(f"/api/projects/{self.team.id}/feature_flags") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.json()["results"]), 2) + sorted_results = sorted(response.json()["results"], key=lambda x: x["key"]) + self.assertEqual(sorted_results[1]["created_by"], None) + self.assertEqual(sorted_results[1]["key"], "flag_role_access") + @patch("posthog.api.feature_flag.report_user_action") def test_my_flags(self, mock_capture): self.client.post( @@ -3539,6 +3570,511 @@ def test_feature_flag_dashboard_already_exists(self): self.assertEquals(len(response_json["analytics_dashboards"]), 1) + @freeze_time("2021-01-01") + @snapshot_clickhouse_queries + def test_creating_static_cohort(self): + flag = FeatureFlag.objects.create( + team=self.team, + rollout_percentage=100, + filters={ + "groups": [{"properties": [{"key": "key", "value": "value", "type": "person"}]}], + "multivariate": None, + }, + name="some feature", + key="some-feature", + created_by=self.user, + ) + + _create_person( + team=self.team, + distinct_ids=[f"person1"], + properties={"key": "value"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person2"], + properties={"key": "value2"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person3"], + properties={"key2": "value3"}, + ) + flush_persons_and_events() + + with snapshot_postgres_queries_context(self), self.settings( + CELERY_TASK_ALWAYS_EAGER=True, PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False + ): + response = self.client.post( + f"/api/projects/{self.team.id}/feature_flags/{flag.id}/create_static_cohort_for_flag", + {}, + format="json", + ) + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + # fires an async task for computation, but celery runs sync in tests + cohort_id = response.json()["cohort"]["id"] + cohort = Cohort.objects.get(id=cohort_id) + self.assertEqual(cohort.name, "Users with feature flag some-feature enabled at 2021-01-01 00:00:00") + self.assertEqual(cohort.count, 1) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 1, response) + + +class TestCohortGenerationForFeatureFlag(APIBaseTest, ClickhouseTestMixin): + def test_creating_static_cohort_with_deleted_flag(self): + FeatureFlag.objects.create( + team=self.team, + rollout_percentage=100, + filters={ + "groups": [{"properties": [{"key": "key", "value": "value", "type": "person"}]}], + "multivariate": None, + }, + name="some feature", + key="some-feature", + created_by=self.user, + deleted=True, + ) + + _create_person( + team=self.team, + distinct_ids=[f"person1"], + properties={"key": "value"}, + ) + flush_persons_and_events() + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with self.assertNumQueries(1): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + # don't even try inserting anything, because invalid flag, so None instead of 0 + self.assertEqual(cohort.count, None) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 0, response) + + def test_creating_static_cohort_with_inactive_flag(self): + FeatureFlag.objects.create( + team=self.team, + rollout_percentage=100, + filters={ + "groups": [{"properties": [{"key": "key", "value": "value", "type": "person"}]}], + "multivariate": None, + }, + name="some feature", + key="some-feature2", + created_by=self.user, + active=False, + ) + + _create_person( + team=self.team, + distinct_ids=[f"person1"], + properties={"key": "value"}, + ) + flush_persons_and_events() + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with self.assertNumQueries(1): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + # don't even try inserting anything, because invalid flag, so None instead of 0 + self.assertEqual(cohort.count, None) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 0, response) + + @freeze_time("2021-01-01") + def test_creating_static_cohort_with_group_flag(self): + FeatureFlag.objects.create( + team=self.team, + rollout_percentage=100, + filters={ + "groups": [{"properties": [{"key": "key", "value": "value", "type": "group", "group_type_index": 1}]}], + "multivariate": None, + "aggregation_group_type_index": 1, + }, + name="some feature", + key="some-feature3", + created_by=self.user, + ) + + _create_person( + team=self.team, + distinct_ids=[f"person1"], + properties={"key": "value"}, + ) + flush_persons_and_events() + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with self.assertNumQueries(1): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature3", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + # don't even try inserting anything, because invalid flag, so None instead of 0 + self.assertEqual(cohort.count, None) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 0, response) + + def test_creating_static_cohort_with_no_person_distinct_ids(self): + FeatureFlag.objects.create( + team=self.team, + rollout_percentage=100, + filters={ + "groups": [{"properties": [], "rollout_percentage": 100}], + "multivariate": None, + }, + name="some feature", + key="some-feature2", + created_by=self.user, + ) + + Person.objects.create(team=self.team) + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with self.assertNumQueries(5): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + # don't even try inserting anything, because invalid flag, so None instead of 0 + self.assertEqual(cohort.count, None) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 0, response) + + def test_creating_static_cohort_with_non_existing_flag(self): + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with self.assertNumQueries(1): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + # don't even try inserting anything, because invalid flag, so None instead of 0 + self.assertEqual(cohort.count, None) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 0, response) + + def test_creating_static_cohort_with_experience_continuity_flag(self): + FeatureFlag.objects.create( + team=self.team, + filters={ + "groups": [ + {"properties": [{"key": "key", "value": "value", "type": "person"}], "rollout_percentage": 50} + ], + "multivariate": None, + }, + name="some feature", + key="some-feature2", + created_by=self.user, + ensure_experience_continuity=True, + ) + + p1 = _create_person(team=self.team, distinct_ids=[f"person1"], properties={"key": "value"}, immediate=True) + _create_person( + team=self.team, + distinct_ids=[f"person2"], + properties={"key": "value"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person3"], + properties={"key": "value"}, + ) + flush_persons_and_events() + + FeatureFlagHashKeyOverride.objects.create( + feature_flag_key="some-feature2", + person=p1, + team=self.team, + hash_key="123", + ) + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + # TODO: Ensure server-side cursors are disabled, since in production we use this with pgbouncer + with snapshot_postgres_queries_context(self), self.assertNumQueries(12): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + self.assertEqual(cohort.count, 1) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 1, response) + + def test_creating_static_cohort_iterator(self): + FeatureFlag.objects.create( + team=self.team, + filters={ + "groups": [ + {"properties": [{"key": "key", "value": "value", "type": "person"}], "rollout_percentage": 100} + ], + "multivariate": None, + }, + name="some feature", + key="some-feature2", + created_by=self.user, + ) + + _create_person( + team=self.team, + distinct_ids=[f"person1"], + properties={"key": "value"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person2"], + properties={"key": "value"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person3"], + properties={"key": "value"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person4"], + properties={"key": "valuu3"}, + ) + flush_persons_and_events() + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + # Extra queries because each batch adds its own queries + with snapshot_postgres_queries_context(self), self.assertNumQueries(17): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk, batchsize=2) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + self.assertEqual(cohort.count, 3) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 3, response) + + # if the batch is big enough, it's fewer queries + with self.assertNumQueries(9): + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk, batchsize=10) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + self.assertEqual(cohort.count, 3) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 3, response) + + def test_creating_static_cohort_with_default_person_properties_adjustment(self): + FeatureFlag.objects.create( + team=self.team, + filters={ + "groups": [ + { + "properties": [{"key": "key", "value": "value", "type": "person", "operator": "icontains"}], + "rollout_percentage": 100, + } + ], + "multivariate": None, + }, + name="some feature", + key="some-feature2", + created_by=self.user, + ensure_experience_continuity=False, + ) + FeatureFlag.objects.create( + team=self.team, + filters={ + "groups": [ + { + "properties": [{"key": "key", "value": "value", "type": "person", "operator": "is_set"}], + "rollout_percentage": 100, + } + ], + "multivariate": None, + }, + name="some feature", + key="some-feature-new", + created_by=self.user, + ensure_experience_continuity=False, + ) + + _create_person(team=self.team, distinct_ids=[f"person1"], properties={"key": "value"}) + _create_person( + team=self.team, + distinct_ids=[f"person2"], + properties={"key": "vaalue"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person3"], + properties={"key22": "value"}, + ) + flush_persons_and_events() + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with snapshot_postgres_queries_context(self), self.assertNumQueries(9): + # no queries to evaluate flags, because all evaluated using override properties + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature2", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + self.assertEqual(cohort.count, 1) + + response = self.client.get(f"/api/cohort/{cohort.pk}/persons") + self.assertEqual(len(response.json()["results"]), 1, response) + + cohort2 = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort2", + ) + + with snapshot_postgres_queries_context(self), self.assertNumQueries(13): + # need to evaluate flags for person3 using db, because is_set operator can't have defaults added. + get_cohort_actors_for_feature_flag(cohort2.pk, "some-feature-new", self.team.pk) + + cohort2.refresh_from_db() + self.assertEqual(cohort2.name, "some cohort2") + self.assertEqual(cohort2.count, 2) + + def test_creating_static_cohort_with_cohort_flag_adds_cohort_props_as_default_too(self): + cohort_nested = Cohort.objects.create( + team=self.team, + filters={ + "properties": { + "type": "OR", + "values": [ + { + "type": "OR", + "values": [ + {"key": "does-not-exist", "value": "none", "type": "person"}, + ], + } + ], + } + }, + ) + cohort_static = Cohort.objects.create( + team=self.team, + is_static=True, + ) + cohort_existing = Cohort.objects.create( + team=self.team, + filters={ + "properties": { + "type": "OR", + "values": [ + { + "type": "OR", + "values": [ + {"key": "group", "value": "none", "type": "person"}, + {"key": "group2", "value": [1, 2, 3], "type": "person"}, + {"key": "id", "value": cohort_static.pk, "type": "cohort"}, + {"key": "id", "value": cohort_nested.pk, "type": "cohort"}, + ], + } + ], + } + }, + name="cohort1", + ) + FeatureFlag.objects.create( + team=self.team, + filters={ + "groups": [ + { + "properties": [{"key": "id", "value": cohort_existing.pk, "type": "cohort"}], + "rollout_percentage": 100, + }, + {"properties": [{"key": "key", "value": "value", "type": "person"}], "rollout_percentage": 100}, + ], + "multivariate": None, + }, + name="some feature", + key="some-feature-new", + created_by=self.user, + ensure_experience_continuity=False, + ) + + _create_person(team=self.team, distinct_ids=[f"person1"], properties={"key": "value"}) + _create_person( + team=self.team, + distinct_ids=[f"person2"], + properties={"group": "none"}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person3"], + properties={"key22": "value", "group2": 2}, + ) + _create_person( + team=self.team, + distinct_ids=[f"person4"], + properties={}, + ) + flush_persons_and_events() + + cohort_static.insert_users_by_list([f"person4"]) + + cohort = Cohort.objects.create( + team=self.team, + is_static=True, + name="some cohort", + ) + + with snapshot_postgres_queries_context(self), self.assertNumQueries(26): + # forced to evaluate flags by going to db, because cohorts need db query to evaluate + get_cohort_actors_for_feature_flag(cohort.pk, "some-feature-new", self.team.pk) + + cohort.refresh_from_db() + self.assertEqual(cohort.name, "some cohort") + self.assertEqual(cohort.count, 4) + class TestBlastRadius(ClickhouseTestMixin, APIBaseTest): @snapshot_clickhouse_queries diff --git a/posthog/api/test/test_feature_flag_utils.py b/posthog/api/test/test_feature_flag_utils.py index dd6108d7ff54c..157fe4f5be9c9 100644 --- a/posthog/api/test/test_feature_flag_utils.py +++ b/posthog/api/test/test_feature_flag_utils.py @@ -51,9 +51,9 @@ def create_cohort(name): cohort_ids = {cohorts["a"].pk, cohorts["b"].pk, cohorts["c"].pk} seen_cohorts_cache = { - str(cohorts["a"].pk): cohorts["a"], - str(cohorts["b"].pk): cohorts["b"], - str(cohorts["c"].pk): cohorts["c"], + cohorts["a"].pk: cohorts["a"], + cohorts["b"].pk: cohorts["b"], + cohorts["c"].pk: cohorts["c"], } # (a)-->(c)-->(b) @@ -68,6 +68,6 @@ def create_cohort(name): def test_empty_cohorts_set(self): cohort_ids: Set[int] = set() - seen_cohorts_cache: Dict[str, Cohort] = {} + seen_cohorts_cache: Dict[int, Cohort] = {} topologically_sorted_cohort_ids = sort_cohorts_topologically(cohort_ids, seen_cohorts_cache) self.assertEqual(topologically_sorted_cohort_ids, []) diff --git a/posthog/api/test/test_organization_feature_flag.py b/posthog/api/test/test_organization_feature_flag.py index 103756d0c4911..78e72269b20bb 100644 --- a/posthog/api/test/test_organization_feature_flag.py +++ b/posthog/api/test/test_organization_feature_flag.py @@ -53,7 +53,7 @@ def test_get_feature_flag_success(self): "email": self.user.email, "is_email_verified": self.user.is_email_verified, }, - "filters": flag.filters, + "filters": flag.get_filters(), "created_at": flag.created_at.strftime("%Y-%m-%dT%H:%M:%S.%f") + "Z", "active": flag.active, } @@ -243,6 +243,29 @@ def test_copy_feature_flag_update_existing(self): set(flag_response.keys()), ) + def test_copy_feature_flag_with_old_legacy_flags(self): + url = f"/api/organizations/{self.organization.id}/feature_flags/copy_flags" + target_project = self.team_2 + + flag_to_copy = FeatureFlag.objects.create( + team=self.team_1, + created_by=self.user, + key="flag-to-copy-here", + filters={}, + rollout_percentage=self.rollout_percentage_to_copy, + ) + + data = { + "feature_flag_key": flag_to_copy.key, + "from_project": self.feature_flag_to_copy.team_id, + "target_project_ids": [target_project.id], + } + response = self.client.post(url, data) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.json()["success"]), 1) + self.assertEqual(len(response.json()["failed"]), 0) + def test_copy_feature_flag_update_override_deleted(self): target_project = self.team_2 target_project_2 = Team.objects.create(organization=self.organization) @@ -581,8 +604,8 @@ def connect(parent, child): # get topological order of the original cohorts original_cohorts_cache = {} for _, cohort in cohorts.items(): - original_cohorts_cache[str(cohort.id)] = cohort - original_cohort_ids = {int(str_id) for str_id in original_cohorts_cache.keys()} + original_cohorts_cache[cohort.id] = cohort + original_cohort_ids = {cohort_id for cohort_id in original_cohorts_cache.keys()} topologically_sorted_original_cohort_ids = sort_cohorts_topologically( original_cohort_ids, original_cohorts_cache ) @@ -593,7 +616,7 @@ def connect(parent, child): def traverse(cohort, index): expected_cohort_id = topologically_sorted_original_cohort_ids_reversed[index] - expected_name = original_cohorts_cache[str(expected_cohort_id)].name + expected_name = original_cohorts_cache[expected_cohort_id].name self.assertEqual(expected_name, cohort.name) prop = cohort.filters["properties"]["values"][0] diff --git a/posthog/api/test/test_search.py b/posthog/api/test/test_search.py index 543d2d5adc048..36f31c8ec9ef4 100644 --- a/posthog/api/test/test_search.py +++ b/posthog/api/test/test_search.py @@ -74,11 +74,7 @@ def test_response_format_and_ids(self): "rank": response.json()["results"][1]["rank"], "type": "insight", "result_id": self.insight_1.short_id, - "extra_fields": { - "derived_name": None, - "name": "second insight", - "description": None, - }, + "extra_fields": {"name": "second insight", "description": None, "filters": {}, "query": None}, }, ) @@ -88,7 +84,7 @@ def test_extra_fields(self): self.assertEqual(response.status_code, 200) self.assertEqual( response.json()["results"][0]["extra_fields"], - {"derived_name": "derived name", "description": None, "name": None}, + {"name": None, "description": None, "filters": {}, "query": None}, ) def test_search_with_fully_invalid_query(self): diff --git a/posthog/api/test/test_signup.py b/posthog/api/test/test_signup.py index e106dd6cbddf2..00c101e4487ee 100644 --- a/posthog/api/test/test_signup.py +++ b/posthog/api/test/test_signup.py @@ -3,9 +3,9 @@ from typing import Dict, Optional, cast from unittest import mock from unittest.mock import ANY, patch +from zoneinfo import ZoneInfo import pytest -from zoneinfo import ZoneInfo from django.core import mail from django.urls.base import reverse from django.utils import timezone @@ -543,6 +543,7 @@ def test_social_signup_with_allowed_domain_on_self_hosted( @patch("posthoganalytics.capture") @mock.patch("ee.billing.billing_manager.BillingManager.update_billing_distinct_ids") + @mock.patch("ee.billing.billing_manager.BillingManager.update_billing_customer_email") @mock.patch("social_core.backends.base.BaseAuth.request") @mock.patch("posthog.api.authentication.get_instance_available_sso_providers") @mock.patch("posthog.tasks.user_identify.identify_task") @@ -553,11 +554,13 @@ def test_social_signup_with_allowed_domain_on_cloud( mock_sso_providers, mock_request, mock_update_distinct_ids, + mock_update_billing_customer_email, mock_capture, ): with self.is_cloud(True): self.run_test_for_allowed_domain(mock_sso_providers, mock_request, mock_capture) assert mock_update_distinct_ids.called_once() + assert mock_update_billing_customer_email.called_once() @mock.patch("social_core.backends.base.BaseAuth.request") @mock.patch("posthog.api.authentication.get_instance_available_sso_providers") diff --git a/posthog/api/test/test_team.py b/posthog/api/test/test_team.py index 4e4b374273229..b1b73389cffab 100644 --- a/posthog/api/test/test_team.py +++ b/posthog/api/test/test_team.py @@ -1,6 +1,7 @@ import json from typing import List, cast -from unittest.mock import ANY, MagicMock, patch +from unittest import mock +from unittest.mock import MagicMock, call, patch from asgiref.sync import sync_to_async from django.core.cache import cache @@ -219,15 +220,16 @@ def test_delete_team_own_second(self, mock_capture: MagicMock, mock_delete_bulky AsyncDeletion.objects.filter(team_id=team.id, deletion_type=DeletionType.Team, key=str(team.id)).count(), 1, ) - mock_capture.assert_called_once_with( - self.user.distinct_id, - "team deleted", - properties={}, - groups={ - "instance": ANY, - "organization": str(self.organization.id), - "project": str(self.team.uuid), - }, + mock_capture.assert_has_calls( + calls=[ + call( + self.user.distinct_id, + "membership level changed", + properties={"new_level": 8, "previous_level": 1}, + groups=mock.ANY, + ), + call(self.user.distinct_id, "team deleted", properties={}, groups=mock.ANY), + ] ) mock_delete_bulky_postgres_data.assert_called_once_with(team_ids=[team.pk]) diff --git a/posthog/batch_exports/http.py b/posthog/batch_exports/http.py index 05127d790ffa8..e896ac70e0be1 100644 --- a/posthog/batch_exports/http.py +++ b/posthog/batch_exports/http.py @@ -2,6 +2,7 @@ from typing import Any import posthoganalytics +import structlog from django.db import transaction from django.utils.timezone import now from rest_framework import mixins, request, response, serializers, viewsets @@ -27,9 +28,10 @@ BatchExportIdError, BatchExportServiceError, BatchExportServiceRPCError, + BatchExportServiceScheduleNotFound, backfill_export, cancel_running_batch_export_backfill, - delete_schedule, + batch_export_delete_schedule, pause_batch_export, sync_batch_export, unpause_batch_export, @@ -49,6 +51,8 @@ from posthog.temporal.common.client import sync_connect from posthog.utils import relative_date_parse +logger = structlog.get_logger(__name__) + def validate_date_input(date_input: Any) -> dt.datetime: """Parse any datetime input as a proper dt.datetime. @@ -320,10 +324,22 @@ def unpause(self, request: request.Request, *args, **kwargs) -> response.Respons return response.Response({"paused": False}) def perform_destroy(self, instance: BatchExport): - """Perform a BatchExport destroy by clearing Temporal and Django state.""" - instance.deleted = True + """Perform a BatchExport destroy by clearing Temporal and Django state. + + If the underlying Temporal Schedule doesn't exist, we ignore the error and proceed with the delete anyways. + The Schedule could have been manually deleted causing Django and Temporal to go out of sync. For whatever reason, + since we are deleting, we assume that we can recover from this state by finishing the delete operation by calling + instance.save(). + """ temporal = sync_connect() - delete_schedule(temporal, str(instance.pk)) + + instance.deleted = True + + try: + batch_export_delete_schedule(temporal, str(instance.pk)) + except BatchExportServiceScheduleNotFound as e: + logger.warning("The Schedule %s could not be deleted as it was not found", e.schedule_id) + instance.save() for backfill in BatchExportBackfill.objects.filter(batch_export=instance): diff --git a/posthog/batch_exports/service.py b/posthog/batch_exports/service.py index 426f67e3a4c09..f0782e5ea12e1 100644 --- a/posthog/batch_exports/service.py +++ b/posthog/batch_exports/service.py @@ -3,6 +3,7 @@ from dataclasses import asdict, dataclass, fields from uuid import UUID +import temporalio from asgiref.sync import async_to_sync from temporalio.client import ( Client, @@ -22,7 +23,7 @@ BatchExportRun, ) from posthog.temporal.common.client import sync_connect -from posthog.temporal.common.schedule import create_schedule, update_schedule, unpause_schedule, pause_schedule +from posthog.temporal.common.schedule import create_schedule, update_schedule, unpause_schedule, pause_schedule, delete_schedule class BatchExportsInputsProtocol(typing.Protocol): @@ -162,6 +163,14 @@ class BatchExportServiceRPCError(BatchExportServiceError): """Exception raised when the underlying Temporal RPC fails.""" +class BatchExportServiceScheduleNotFound(BatchExportServiceRPCError): + """Exception raised when the underlying Temporal RPC fails because a schedule was not found.""" + + def __init__(self, schedule_id: str): + self.schedule_id = schedule_id + super().__init__(f"The Temporal Schedule {schedule_id} was not found (maybe it was deleted?)") + + def pause_batch_export(temporal: Client, batch_export_id: str, note: str | None = None) -> None: """Pause this BatchExport. @@ -231,6 +240,17 @@ def unpause_batch_export( backfill_export(temporal, batch_export_id, start_at, end_at) +@async_to_sync +async def batch_export_delete_schedule(temporal: Client, schedule_id: str) -> None: + """Delete a Temporal Schedule.""" + try: + await delete_schedule(temporal, schedule_id) + except temporalio.service.RPCError as e: + if e.status == temporalio.service.RPCStatusCode.NOT_FOUND: + raise BatchExportServiceScheduleNotFound(schedule_id) + else: + raise BatchExportServiceRPCError() from e + @async_to_sync async def cancel_running_batch_export_backfill(temporal: Client, workflow_id: str) -> None: """Delete a running BatchExportBackfill. diff --git a/posthog/clickhouse/client/execute_async.py b/posthog/clickhouse/client/execute_async.py index fc9e292b08ee4..7e42d52d4836c 100644 --- a/posthog/clickhouse/client/execute_async.py +++ b/posthog/clickhouse/client/execute_async.py @@ -81,7 +81,6 @@ def enqueue_process_query_task( query_json, query_id=None, refresh_requested=False, - in_export_context=False, bypass_celery=False, force=False, ): @@ -115,12 +114,10 @@ def enqueue_process_query_task( if bypass_celery: # Call directly ( for testing ) - process_query_task( - team_id, query_id, query_json, in_export_context=in_export_context, refresh_requested=refresh_requested - ) + process_query_task(team_id, query_id, query_json, in_export_context=True, refresh_requested=refresh_requested) else: task = process_query_task.delay( - team_id, query_id, query_json, in_export_context=in_export_context, refresh_requested=refresh_requested + team_id, query_id, query_json, in_export_context=True, refresh_requested=refresh_requested ) query_status.task_id = task.id redis_client.set(key, query_status.model_dump_json(), ex=REDIS_STATUS_TTL_SECONDS) diff --git a/posthog/clickhouse/client/test/__snapshots__/test_execute_async.ambr b/posthog/clickhouse/client/test/__snapshots__/test_execute_async.ambr new file mode 100644 index 0000000000000..282191d2015c7 --- /dev/null +++ b/posthog/clickhouse/client/test/__snapshots__/test_execute_async.ambr @@ -0,0 +1,8 @@ +# name: ClickhouseClientTestCase.test_async_query_client + ' + SELECT plus(1, 1) + LIMIT 10000 SETTINGS readonly=2, + max_execution_time=600, + allow_experimental_object_type=1 + ' +--- diff --git a/posthog/clickhouse/client/test/test_execute_async.py b/posthog/clickhouse/client/test/test_execute_async.py index 1ab4bf49e03d3..4958c23b3f0a0 100644 --- a/posthog/clickhouse/client/test/test_execute_async.py +++ b/posthog/clickhouse/client/test/test_execute_async.py @@ -7,7 +7,7 @@ from posthog.client import sync_execute from posthog.hogql.errors import HogQLException from posthog.models import Organization, Team -from posthog.test.base import ClickhouseTestMixin +from posthog.test.base import ClickhouseTestMixin, snapshot_clickhouse_queries def build_query(sql): @@ -23,6 +23,7 @@ def setUp(self): self.team = Team.objects.create(organization=self.organization) self.team_id = self.team.pk + @snapshot_clickhouse_queries def test_async_query_client(self): query = build_query("SELECT 1+1") team_id = self.team_id diff --git a/posthog/event_usage.py b/posthog/event_usage.py index fa69f0c23662b..7cd1945d37df4 100644 --- a/posthog/event_usage.py +++ b/posthog/event_usage.py @@ -196,6 +196,26 @@ def report_bulk_invited( ) +def report_user_organization_membership_level_changed( + user: User, + organization: Organization, + new_level: int, + previous_level: int, +) -> None: + """ + Triggered after a user's membership level in an organization is changed. + """ + posthoganalytics.capture( + user.distinct_id, + "membership level changed", + properties={ + "new_level": new_level, + "previous_level": previous_level, + }, + groups=groups(organization), + ) + + def report_user_action(user: User, event: str, properties: Dict = {}): posthoganalytics.capture( user.distinct_id, diff --git a/posthog/hogql/query.py b/posthog/hogql/query.py index c7e8c82713b15..751b9fb46b860 100644 --- a/posthog/hogql/query.py +++ b/posthog/hogql/query.py @@ -22,6 +22,8 @@ from posthog.client import sync_execute from posthog.schema import HogQLQueryResponse, HogQLFilters, HogQLQueryModifiers +EXPORT_CONTEXT_MAX_EXECUTION_TIME = 600 + def execute_hogql_query( query: Union[str, ast.SelectQuery, ast.SelectUnionQuery], @@ -119,6 +121,10 @@ def execute_hogql_query( ) ) + settings = settings or HogQLGlobalSettings() + if in_export_context: + settings.max_execution_time = EXPORT_CONTEXT_MAX_EXECUTION_TIME + # Print the ClickHouse SQL query with timings.measure("print_ast"): clickhouse_context = HogQLContext( @@ -131,7 +137,7 @@ def execute_hogql_query( select_query, context=clickhouse_context, dialect="clickhouse", - settings=settings or HogQLGlobalSettings(), + settings=settings, ) timings_dict = timings.to_dict() diff --git a/posthog/hogql/test/__snapshots__/test_resolver.ambr b/posthog/hogql/test/__snapshots__/test_resolver.ambr new file mode 100644 index 0000000000000..78223c03c2b66 --- /dev/null +++ b/posthog/hogql/test/__snapshots__/test_resolver.ambr @@ -0,0 +1,2984 @@ +# name: TestResolver.test_asterisk_expander_from_subquery_table + ' + { + select: [ + { + chain: [ + "uuid" + ] + type: { + name: "uuid" + table_type: { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: { + name: "$group_0" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + }, + $group_1: { + name: "$group_1" + table_type: + }, + $group_2: { + name: "$group_2" + table_type: + }, + $group_3: { + name: "$group_3" + table_type: + }, + $group_4: { + name: "$group_4" + table_type: + }, + $session_id: { + name: "$session_id" + table_type: + }, + created_at: { + name: "created_at" + table_type: + }, + distinct_id: { + name: "distinct_id" + table_type: + }, + elements_chain: { + name: "elements_chain" + table_type: + }, + event: { + name: "event" + table_type: + }, + properties: { + name: "properties" + table_type: + }, + timestamp: { + name: "timestamp" + table_type: + }, + uuid: { + name: "uuid" + table_type: + } + } + ctes: {} + tables: { + events: + } + } + } + }, + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: + } + }, + { + chain: [ + "properties" + ] + type: { + name: "properties" + table_type: + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + }, + { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: + } + }, + { + chain: [ + "elements_chain" + ] + type: { + name: "elements_chain" + table_type: + } + }, + { + chain: [ + "created_at" + ] + type: { + name: "created_at" + table_type: + } + }, + { + chain: [ + "$session_id" + ] + type: { + name: "$session_id" + table_type: + } + }, + { + chain: [ + "$group_0" + ] + type: { + name: "$group_0" + table_type: + } + }, + { + chain: [ + "$group_1" + ] + type: { + name: "$group_1" + table_type: + } + }, + { + chain: [ + "$group_2" + ] + type: { + name: "$group_2" + table_type: + } + }, + { + chain: [ + "$group_3" + ] + type: { + name: "$group_3" + table_type: + } + }, + { + chain: [ + "$group_4" + ] + type: { + name: "$group_4" + table_type: + } + } + ] + select_from: { + table: { + select: [ + { + chain: [ + "uuid" + ] + type: + }, + { + chain: [ + "event" + ] + type: + }, + { + chain: [ + "properties" + ] + type: + }, + { + chain: [ + "timestamp" + ] + type: + }, + { + chain: [ + "distinct_id" + ] + type: + }, + { + chain: [ + "elements_chain" + ] + type: + }, + { + chain: [ + "created_at" + ] + type: + }, + { + chain: [ + "$session_id" + ] + type: + }, + { + chain: [ + "$group_0" + ] + type: + }, + { + chain: [ + "$group_1" + ] + type: + }, + { + chain: [ + "$group_2" + ] + type: + }, + { + chain: [ + "$group_3" + ] + type: + }, + { + chain: [ + "$group_4" + ] + type: + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [ + + ] + columns: { + $group_0: , + $group_1: , + $group_2: , + $group_3: , + $group_4: , + $session_id: , + created_at: , + distinct_id: , + elements_chain: , + event: , + properties: , + timestamp: , + uuid: + } + ctes: {} + tables: {} + } + } + ' +--- +# name: TestResolver.test_asterisk_expander_select_union + ' + { + select: [ + { + chain: [ + "uuid" + ] + type: { + name: "uuid" + table_type: { + types: [ + { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: { + name: "$group_0" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + }, + $group_1: { + name: "$group_1" + table_type: + }, + $group_2: { + name: "$group_2" + table_type: + }, + $group_3: { + name: "$group_3" + table_type: + }, + $group_4: { + name: "$group_4" + table_type: + }, + $session_id: { + name: "$session_id" + table_type: + }, + created_at: { + name: "created_at" + table_type: + }, + distinct_id: { + name: "distinct_id" + table_type: + }, + elements_chain: { + name: "elements_chain" + table_type: + }, + event: { + name: "event" + table_type: + }, + properties: { + name: "properties" + table_type: + }, + timestamp: { + name: "timestamp" + table_type: + }, + uuid: { + name: "uuid" + table_type: + } + } + ctes: {} + tables: { + events: + } + }, + { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: { + name: "$group_0" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + }, + $group_1: { + name: "$group_1" + table_type: + }, + $group_2: { + name: "$group_2" + table_type: + }, + $group_3: { + name: "$group_3" + table_type: + }, + $group_4: { + name: "$group_4" + table_type: + }, + $session_id: { + name: "$session_id" + table_type: + }, + created_at: { + name: "created_at" + table_type: + }, + distinct_id: { + name: "distinct_id" + table_type: + }, + elements_chain: { + name: "elements_chain" + table_type: + }, + event: { + name: "event" + table_type: + }, + properties: { + name: "properties" + table_type: + }, + timestamp: { + name: "timestamp" + table_type: + }, + uuid: { + name: "uuid" + table_type: + } + } + ctes: {} + tables: { + events: + } + } + ] + } + } + }, + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: + } + }, + { + chain: [ + "properties" + ] + type: { + name: "properties" + table_type: + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + }, + { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: + } + }, + { + chain: [ + "elements_chain" + ] + type: { + name: "elements_chain" + table_type: + } + }, + { + chain: [ + "created_at" + ] + type: { + name: "created_at" + table_type: + } + }, + { + chain: [ + "$session_id" + ] + type: { + name: "$session_id" + table_type: + } + }, + { + chain: [ + "$group_0" + ] + type: { + name: "$group_0" + table_type: + } + }, + { + chain: [ + "$group_1" + ] + type: { + name: "$group_1" + table_type: + } + }, + { + chain: [ + "$group_2" + ] + type: { + name: "$group_2" + table_type: + } + }, + { + chain: [ + "$group_3" + ] + type: { + name: "$group_3" + table_type: + } + }, + { + chain: [ + "$group_4" + ] + type: { + name: "$group_4" + table_type: + } + } + ] + select_from: { + table: { + select_queries: [ + { + select: [ + { + chain: [ + "uuid" + ] + type: + }, + { + chain: [ + "event" + ] + type: + }, + { + chain: [ + "properties" + ] + type: + }, + { + chain: [ + "timestamp" + ] + type: + }, + { + chain: [ + "distinct_id" + ] + type: + }, + { + chain: [ + "elements_chain" + ] + type: + }, + { + chain: [ + "created_at" + ] + type: + }, + { + chain: [ + "$session_id" + ] + type: + }, + { + chain: [ + "$group_0" + ] + type: + }, + { + chain: [ + "$group_1" + ] + type: + }, + { + chain: [ + "$group_2" + ] + type: + }, + { + chain: [ + "$group_3" + ] + type: + }, + { + chain: [ + "$group_4" + ] + type: + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: + }, + { + select: [ + { + chain: [ + "uuid" + ] + type: + }, + { + chain: [ + "event" + ] + type: + }, + { + chain: [ + "properties" + ] + type: + }, + { + chain: [ + "timestamp" + ] + type: + }, + { + chain: [ + "distinct_id" + ] + type: + }, + { + chain: [ + "elements_chain" + ] + type: + }, + { + chain: [ + "created_at" + ] + type: + }, + { + chain: [ + "$session_id" + ] + type: + }, + { + chain: [ + "$group_0" + ] + type: + }, + { + chain: [ + "$group_1" + ] + type: + }, + { + chain: [ + "$group_2" + ] + type: + }, + { + chain: [ + "$group_3" + ] + type: + }, + { + chain: [ + "$group_4" + ] + type: + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: + } + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [ + + ] + columns: { + $group_0: , + $group_1: , + $group_2: , + $group_3: , + $group_4: , + $session_id: , + created_at: , + distinct_id: , + elements_chain: , + event: , + properties: , + timestamp: , + uuid: + } + ctes: {} + tables: {} + } + } + ' +--- +# name: TestResolver.test_asterisk_expander_subquery + ' + { + select: [ + { + chain: [ + "a" + ] + type: { + name: "a" + table_type: { + aliases: { + a: { + alias: "a" + type: { + data_type: "int" + } + }, + b: { + alias: "b" + type: { + data_type: "int" + } + } + } + anonymous_tables: [] + columns: { + a: , + b: + } + ctes: {} + tables: {} + } + } + }, + { + chain: [ + "b" + ] + type: { + name: "b" + table_type: + } + } + ] + select_from: { + table: { + select: [ + { + alias: "a" + expr: { + type: + value: 1 + } + type: + }, + { + alias: "b" + expr: { + type: + value: 2 + } + type: + } + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [ + + ] + columns: { + a: , + b: + } + ctes: {} + tables: {} + } + } + ' +--- +# name: TestResolver.test_asterisk_expander_subquery_alias + ' + { + select: [ + { + chain: [ + "a" + ] + type: { + name: "a" + table_type: { + alias: "x" + select_query_type: { + aliases: { + a: { + alias: "a" + type: { + data_type: "int" + } + }, + b: { + alias: "b" + type: { + data_type: "int" + } + } + } + anonymous_tables: [] + columns: { + a: , + b: + } + ctes: {} + tables: {} + } + } + } + }, + { + chain: [ + "b" + ] + type: { + name: "b" + table_type: + } + } + ] + select_from: { + alias: "x" + table: { + select: [ + { + alias: "a" + expr: { + type: + value: 1 + } + type: + }, + { + alias: "b" + expr: { + type: + value: 2 + } + type: + } + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + a: , + b: + } + ctes: {} + tables: { + x: + } + } + } + ' +--- +# name: TestResolver.test_asterisk_expander_table + ' + { + select: [ + { + chain: [ + "uuid" + ] + type: { + name: "uuid" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: + } + }, + { + chain: [ + "properties" + ] + type: { + name: "properties" + table_type: + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + }, + { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: + } + }, + { + chain: [ + "elements_chain" + ] + type: { + name: "elements_chain" + table_type: + } + }, + { + chain: [ + "created_at" + ] + type: { + name: "created_at" + table_type: + } + }, + { + chain: [ + "$session_id" + ] + type: { + name: "$session_id" + table_type: + } + }, + { + chain: [ + "$group_0" + ] + type: { + name: "$group_0" + table_type: + } + }, + { + chain: [ + "$group_1" + ] + type: { + name: "$group_1" + table_type: + } + }, + { + chain: [ + "$group_2" + ] + type: { + name: "$group_2" + table_type: + } + }, + { + chain: [ + "$group_3" + ] + type: { + name: "$group_3" + table_type: + } + }, + { + chain: [ + "$group_4" + ] + type: { + name: "$group_4" + table_type: + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: , + $group_1: , + $group_2: , + $group_3: , + $group_4: , + $session_id: , + created_at: , + distinct_id: , + elements_chain: , + event: , + properties: , + timestamp: , + uuid: + } + ctes: {} + tables: { + events: + } + } + } + ' +--- +# name: TestResolver.test_asterisk_expander_table_alias + ' + { + select: [ + { + chain: [ + "uuid" + ] + type: { + name: "uuid" + table_type: { + alias: "e" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + }, + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: + } + }, + { + chain: [ + "properties" + ] + type: { + name: "properties" + table_type: + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + }, + { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: + } + }, + { + chain: [ + "elements_chain" + ] + type: { + name: "elements_chain" + table_type: + } + }, + { + chain: [ + "created_at" + ] + type: { + name: "created_at" + table_type: + } + }, + { + chain: [ + "$session_id" + ] + type: { + name: "$session_id" + table_type: + } + }, + { + chain: [ + "$group_0" + ] + type: { + name: "$group_0" + table_type: + } + }, + { + chain: [ + "$group_1" + ] + type: { + name: "$group_1" + table_type: + } + }, + { + chain: [ + "$group_2" + ] + type: { + name: "$group_2" + table_type: + } + }, + { + chain: [ + "$group_3" + ] + type: { + name: "$group_3" + table_type: + } + }, + { + chain: [ + "$group_4" + ] + type: { + name: "$group_4" + table_type: + } + } + ] + select_from: { + alias: "e" + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: , + $group_1: , + $group_2: , + $group_3: , + $group_4: , + $session_id: , + created_at: , + distinct_id: , + elements_chain: , + event: , + properties: , + timestamp: , + uuid: + } + ctes: {} + tables: { + e: + } + } + } + ' +--- +# name: TestResolver.test_call_type + ' + { + select: [ + { + args: [ + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + ] + distinct: False + name: "max" + type: { + arg_types: [ + { + data_type: "datetime" + } + ] + name: "max" + return_type: { + data_type: "unknown" + } + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: {} + ctes: {} + tables: { + events: + } + } + } + ' +--- +# name: TestResolver.test_resolve_boolean_operation_types + ' + { + select: [ + { + exprs: [ + { + type: { + data_type: "int" + } + value: 1 + }, + { + type: { + data_type: "int" + } + value: 1 + } + ] + type: { + data_type: "bool" + } + }, + { + exprs: [ + { + type: { + data_type: "int" + } + value: 1 + }, + { + type: { + data_type: "int" + } + value: 1 + } + ] + type: { + data_type: "bool" + } + }, + { + expr: { + type: { + data_type: "bool" + } + value: True + } + type: { + data_type: "bool" + } + } + ] + type: { + aliases: {} + anonymous_tables: [] + columns: {} + ctes: {} + tables: {} + } + } + ' +--- +# name: TestResolver.test_resolve_constant_type + ' + { + select: [ + { + type: { + data_type: "int" + } + value: 1 + }, + { + type: { + data_type: "str" + } + value: "boo" + }, + { + type: { + data_type: "bool" + } + value: True + }, + { + type: { + data_type: "float" + } + value: 1.1232 + }, + { + type: { + data_type: "unknown" + } + }, + { + type: { + data_type: "date" + } + value: 2020-01-10 + }, + { + type: { + data_type: "datetime" + } + value: 2020-01-10 00:00:00+00:00 + }, + { + type: { + data_type: "uuid" + } + value: 00000000-0000-4000-8000-000000000000 + }, + { + type: { + data_type: "array" + item_type: { + data_type: "unknown" + } + } + value: [] + }, + { + type: { + data_type: "array" + item_type: { + data_type: "int" + } + } + value: [ + 1, + 2 + ] + }, + { + type: { + data_type: "tuple" + item_types: [ + { + data_type: "int" + }, + { + data_type: "int" + }, + { + data_type: "int" + } + ] + } + value: (1, 2, 3) + } + ] + type: { + aliases: {} + anonymous_tables: [] + columns: {} + ctes: {} + tables: {} + } + } + ' +--- +# name: TestResolver.test_resolve_events_table + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "events", + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + timestamp: + } + ctes: {} + tables: { + events: + } + } + where: { + left: { + chain: [ + "events", + "event" + ] + type: { + name: "event" + table_type: + } + } + op: "==" + right: { + type: { + data_type: "str" + } + value: "test" + } + type: { + data_type: "bool" + } + } + } + ' +--- +# name: TestResolver.test_resolve_events_table_alias + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + alias: "e" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + }, + { + chain: [ + "e", + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + ] + select_from: { + alias: "e" + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + timestamp: + } + ctes: {} + tables: { + e: + } + } + where: { + left: { + chain: [ + "e", + "event" + ] + type: { + name: "event" + table_type: + } + } + op: "==" + right: { + type: { + data_type: "str" + } + value: "test" + } + type: { + data_type: "bool" + } + } + } + ' +--- +# name: TestResolver.test_resolve_events_table_column_alias + ' + { + select: [ + { + alias: "ee" + expr: { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + alias: "e" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + } + type: { + alias: "ee" + type: + } + }, + { + chain: [ + "ee" + ] + type: + }, + { + alias: "e" + expr: { + chain: [ + "ee" + ] + type: + } + type: { + alias: "e" + type: + } + }, + { + chain: [ + "e", + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + ] + select_from: { + alias: "e" + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: { + e: , + ee: + } + anonymous_tables: [] + columns: { + e: , + ee: , + timestamp: + } + ctes: {} + tables: { + e: + } + } + where: { + left: { + chain: [ + "e", + "event" + ] + type: { + name: "event" + table_type: + } + } + op: "==" + right: { + type: { + data_type: "str" + } + value: "test" + } + type: { + data_type: "bool" + } + } + } + ' +--- +# name: TestResolver.test_resolve_events_table_column_alias_inside_subquery + ' + { + select: [ + { + chain: [ + "b" + ] + type: { + name: "b" + table_type: { + alias: "e" + select_query_type: { + aliases: { + b: { + alias: "b" + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + c: { + alias: "c" + type: { + name: "timestamp" + table_type: + } + } + } + anonymous_tables: [] + columns: { + b: , + c: + } + ctes: {} + tables: { + events: + } + } + } + } + } + ] + select_from: { + alias: "e" + table: { + select: [ + { + alias: "b" + expr: { + chain: [ + "event" + ] + type: + } + type: + }, + { + alias: "c" + expr: { + chain: [ + "timestamp" + ] + type: + } + type: + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + b: + } + ctes: {} + tables: { + e: + } + } + where: { + left: { + chain: [ + "e", + "b" + ] + type: { + name: "b" + table_type: + } + } + op: "==" + right: { + type: { + data_type: "str" + } + value: "test" + } + type: { + data_type: "bool" + } + } + } + ' +--- +# name: TestResolver.test_resolve_lazy_events_pdi_person_table + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "pdi", + "person", + "id" + ] + type: { + name: "id" + table_type: { + field: "person" + lazy_join: { + from_field: "person_id", + join_function: , + join_table: { + fields: { + created_at: {}, + id: {}, + is_identified: {}, + pdi: {}, + properties: {}, + team_id: {} + } + } + } + table_type: { + field: "pdi" + lazy_join: { + from_field: "distinct_id", + join_function: , + join_table: { + fields: { + distinct_id: {}, + person: {}, + person_id: {}, + team_id: {} + } + } + } + table_type: + } + } + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + id: + } + ctes: {} + tables: { + events: + } + } + } + ' +--- +# name: TestResolver.test_resolve_lazy_events_pdi_person_table_aliased + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + alias: "e" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + }, + { + chain: [ + "e", + "pdi", + "person", + "id" + ] + type: { + name: "id" + table_type: { + field: "person" + lazy_join: { + from_field: "person_id", + join_function: , + join_table: { + fields: { + created_at: {}, + id: {}, + is_identified: {}, + pdi: {}, + properties: {}, + team_id: {} + } + } + } + table_type: { + field: "pdi" + lazy_join: { + from_field: "distinct_id", + join_function: , + join_table: { + fields: { + distinct_id: {}, + person: {}, + person_id: {}, + team_id: {} + } + } + } + table_type: + } + } + } + } + ] + select_from: { + alias: "e" + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + id: + } + ctes: {} + tables: { + e: + } + } + } + ' +--- +# name: TestResolver.test_resolve_lazy_events_pdi_table + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "pdi", + "person_id" + ] + type: { + name: "person_id" + table_type: { + field: "pdi" + lazy_join: { + from_field: "distinct_id", + join_function: , + join_table: { + fields: { + distinct_id: {}, + person: {}, + person_id: {}, + team_id: {} + } + } + } + table_type: + } + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + person_id: + } + ctes: {} + tables: { + events: + } + } + } + ' +--- +# name: TestResolver.test_resolve_lazy_events_pdi_table_aliased + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + alias: "e" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + }, + { + chain: [ + "e", + "pdi", + "person_id" + ] + type: { + name: "person_id" + table_type: { + field: "pdi" + lazy_join: { + from_field: "distinct_id", + join_function: , + join_table: { + fields: { + distinct_id: {}, + person: {}, + person_id: {}, + team_id: {} + } + } + } + table_type: + } + } + } + ] + select_from: { + alias: "e" + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + person_id: + } + ctes: {} + tables: { + e: + } + } + } + ' +--- +# name: TestResolver.test_resolve_lazy_pdi_person_table + ' + { + select: [ + { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: { + table: { + fields: { + distinct_id: {}, + person: {}, + person_id: {}, + team_id: {} + } + } + } + } + }, + { + chain: [ + "person", + "id" + ] + type: { + name: "id" + table_type: { + field: "person" + lazy_join: { + from_field: "person_id", + join_function: , + join_table: { + fields: { + created_at: {}, + id: {}, + is_identified: {}, + pdi: {}, + properties: {}, + team_id: {} + } + } + } + table_type: + } + } + } + ] + select_from: { + table: { + chain: [ + "person_distinct_ids" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + distinct_id: , + id: + } + ctes: {} + tables: { + person_distinct_ids: + } + } + } + ' +--- +# name: TestResolver.test_resolve_union_all + ' + { + select_queries: [ + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + timestamp: + } + ctes: {} + tables: { + events: + } + } + }, + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + timestamp: + } + ctes: {} + tables: { + events: + } + } + } + ] + type: { + types: [ + , + + ] + } + } + ' +--- +# name: TestResolver.test_resolve_virtual_events_poe + ' + { + select: [ + { + chain: [ + "event" + ] + type: { + name: "event" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + override: {}, + override_person_id: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + }, + { + chain: [ + "poe", + "id" + ] + type: { + name: "id" + table_type: { + field: "poe" + table_type: + virtual_table: { + fields: { + created_at: {}, + id: {}, + properties: {} + } + } + } + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + event: , + id: + } + ctes: {} + tables: { + events: + } + } + } + ' +--- diff --git a/posthog/hogql/test/test_resolver.py b/posthog/hogql/test/test_resolver.py index f2ee1d812ea65..069e633e0a457 100644 --- a/posthog/hogql/test/test_resolver.py +++ b/posthog/hogql/test/test_resolver.py @@ -1,6 +1,6 @@ from datetime import timezone, datetime, date from typing import Optional, Dict, cast - +import pytest from django.test import override_settings from uuid import UUID @@ -10,12 +10,12 @@ from posthog.hogql.context import HogQLContext from posthog.hogql.database.database import create_hogql_database from posthog.hogql.database.models import ( - LazyJoin, FieldTraverser, StringJSONDatabaseField, StringDatabaseField, DateTimeDatabaseField, ) +from posthog.hogql.test.utils import pretty_dataclasses from posthog.hogql.visitor import clone_expr from posthog.hogql.parser import parse_select from posthog.hogql.printer import print_ast, print_prepared_ast @@ -44,42 +44,11 @@ def setUp(self): self.database = create_hogql_database(self.team.pk) self.context = HogQLContext(database=self.database, team_id=self.team.pk) + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_events_table(self): expr = self._select("SELECT event, events.timestamp FROM events WHERE events.event = 'test'") expr = resolve_types(expr, self.context) - - events_table_type = ast.TableType(table=self.database.events) - event_field_type = ast.FieldType(name="event", table_type=events_table_type) - timestamp_field_type = ast.FieldType(name="timestamp", table_type=events_table_type) - select_query_type = ast.SelectQueryType( - columns={"event": event_field_type, "timestamp": timestamp_field_type}, - tables={"events": events_table_type}, - ) - - expected = ast.SelectQuery( - select=[ - ast.Field(chain=["event"], type=event_field_type), - ast.Field(chain=["events", "timestamp"], type=timestamp_field_type), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - type=events_table_type, - ), - where=ast.CompareOperation( - left=ast.Field(chain=["events", "event"], type=event_field_type), - op=ast.CompareOperationOp.Eq, - right=ast.Constant(value="test", type=ast.StringType()), - type=ast.BooleanType(), - ), - type=select_query_type, - ) - - # asserting individually to help debug if something is off - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot def test_will_not_run_twice(self): expr = self._select("SELECT event, events.timestamp FROM events WHERE events.event = 'test'") @@ -91,186 +60,23 @@ def test_will_not_run_twice(self): "Type already resolved for SelectQuery (SelectQueryType). Can't run again.", ) + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_events_table_alias(self): expr = self._select("SELECT event, e.timestamp FROM events e WHERE e.event = 'test'") expr = resolve_types(expr, self.context) + assert pretty_dataclasses(expr) == self.snapshot - events_table_type = ast.TableType(table=self.database.events) - events_table_alias_type = ast.TableAliasType(alias="e", table_type=events_table_type) - event_field_type = ast.FieldType(name="event", table_type=events_table_alias_type) - timestamp_field_type = ast.FieldType(name="timestamp", table_type=events_table_alias_type) - select_query_type = ast.SelectQueryType( - columns={"event": event_field_type, "timestamp": timestamp_field_type}, - tables={"e": events_table_alias_type}, - ) - - expected = ast.SelectQuery( - select=[ - ast.Field(chain=["event"], type=event_field_type), - ast.Field(chain=["e", "timestamp"], type=timestamp_field_type), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - alias="e", - type=events_table_alias_type, - ), - where=ast.CompareOperation( - left=ast.Field(chain=["e", "event"], type=event_field_type), - op=ast.CompareOperationOp.Eq, - right=ast.Constant(value="test", type=ast.StringType()), - type=ast.BooleanType(), - ), - type=select_query_type, - ) - - # asserting individually to help debug if something is off - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) - + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_events_table_column_alias(self): expr = self._select("SELECT event as ee, ee, ee as e, e.timestamp FROM events e WHERE e.event = 'test'") expr = resolve_types(expr, self.context) + assert pretty_dataclasses(expr) == self.snapshot - events_table_type = ast.TableType(table=self.database.events) - events_table_alias_type = ast.TableAliasType(alias="e", table_type=events_table_type) - event_field_type = ast.FieldType(name="event", table_type=events_table_alias_type) - timestamp_field_type = ast.FieldType(name="timestamp", table_type=events_table_alias_type) - - select_query_type = ast.SelectQueryType( - aliases={ - "ee": ast.FieldAliasType(alias="ee", type=event_field_type), - "e": ast.FieldAliasType( - alias="e", - type=ast.FieldAliasType(alias="ee", type=event_field_type), - ), - }, - columns={ - "ee": ast.FieldAliasType(alias="ee", type=event_field_type), - "e": ast.FieldAliasType( - alias="e", - type=ast.FieldAliasType(alias="ee", type=event_field_type), - ), - "timestamp": timestamp_field_type, - }, - tables={"e": events_table_alias_type}, - ) - - expected = ast.SelectQuery( - select=[ - ast.Alias( - alias="ee", - expr=ast.Field(chain=["event"], type=event_field_type), - type=select_query_type.aliases["ee"], - ), - ast.Field(chain=["ee"], type=select_query_type.aliases["ee"]), - ast.Alias( - alias="e", - expr=ast.Field(chain=["ee"], type=select_query_type.aliases["ee"]), - type=select_query_type.aliases["e"], # is ee ? - ), - ast.Field(chain=["e", "timestamp"], type=timestamp_field_type), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - alias="e", - type=select_query_type.tables["e"], - ), - where=ast.CompareOperation( - left=ast.Field(chain=["e", "event"], type=event_field_type), - op=ast.CompareOperationOp.Eq, - right=ast.Constant(value="test", type=ast.StringType()), - type=ast.BooleanType(), - ), - type=select_query_type, - ) - # asserting individually to help debug if something is off - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) - + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_events_table_column_alias_inside_subquery(self): expr = self._select("SELECT b FROM (select event as b, timestamp as c from events) e WHERE e.b = 'test'") expr = resolve_types(expr, self.context) - inner_events_table_type = ast.TableType(table=self.database.events) - inner_event_field_type = ast.FieldAliasType( - alias="b", - type=ast.FieldType(name="event", table_type=inner_events_table_type), - ) - timestamp_field_type = ast.FieldType(name="timestamp", table_type=inner_events_table_type) - timstamp_alias_type = ast.FieldAliasType(alias="c", type=timestamp_field_type) - inner_select_type = ast.SelectQueryType( - aliases={ - "b": inner_event_field_type, - "c": ast.FieldAliasType(alias="c", type=timestamp_field_type), - }, - columns={ - "b": inner_event_field_type, - "c": ast.FieldAliasType(alias="c", type=timestamp_field_type), - }, - tables={ - "events": inner_events_table_type, - }, - ) - select_alias_type = ast.SelectQueryAliasType(alias="e", select_query_type=inner_select_type) - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["b"], - type=ast.FieldType( - name="b", - table_type=ast.SelectQueryAliasType(alias="e", select_query_type=inner_select_type), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.SelectQuery( - select=[ - ast.Alias( - alias="b", - expr=ast.Field(chain=["event"], type=inner_event_field_type.type), - type=inner_event_field_type, - ), - ast.Alias( - alias="c", - expr=ast.Field(chain=["timestamp"], type=timestamp_field_type), - type=timstamp_alias_type, - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=inner_events_table_type), - type=inner_events_table_type, - ), - type=inner_select_type, - ), - alias="e", - type=select_alias_type, - ), - where=ast.CompareOperation( - left=ast.Field( - chain=["e", "b"], - type=ast.FieldType(name="b", table_type=select_alias_type), - ), - op=ast.CompareOperationOp.Eq, - right=ast.Constant(value="test", type=ast.StringType()), - type=ast.BooleanType(), - ), - type=ast.SelectQueryType( - aliases={}, - columns={"b": ast.FieldType(name="b", table_type=select_alias_type)}, - tables={"e": select_alias_type}, - ), - ) - # asserting individually to help debug if something is off - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot def test_resolve_subquery_no_field_access(self): # From ClickHouse's GitHub: "Aliases defined outside of subquery are not visible in subqueries (but see below)." @@ -281,6 +87,7 @@ def test_resolve_subquery_no_field_access(self): expr = resolve_types(expr, self.context) self.assertEqual(str(e.exception), "Unable to resolve field: e") + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_constant_type(self): with freeze_time("2020-01-10 00:00:00"): expr = self._select( @@ -295,66 +102,13 @@ def test_resolve_constant_type(self): }, ) expr = resolve_types(expr, self.context) - expected = ast.SelectQuery( - select=[ - ast.Constant(value=1, type=ast.IntegerType()), - ast.Constant(value="boo", type=ast.StringType()), - ast.Constant(value=True, type=ast.BooleanType()), - ast.Constant(value=1.1232, type=ast.FloatType()), - ast.Constant(value=None, type=ast.UnknownType()), - ast.Constant(value=date(2020, 1, 10), type=ast.DateType()), - ast.Constant( - value=datetime(2020, 1, 10, 0, 0, 0, tzinfo=timezone.utc), - type=ast.DateTimeType(), - ), - ast.Constant( - value=UUID("00000000-0000-4000-8000-000000000000"), - type=ast.UUIDType(), - ), - ast.Constant(value=[], type=ast.ArrayType(item_type=ast.UnknownType())), - ast.Constant(value=[1, 2], type=ast.ArrayType(item_type=ast.IntegerType())), - ast.Constant( - value=(1, 2, 3), - type=ast.TupleType( - item_types=[ - ast.IntegerType(), - ast.IntegerType(), - ast.IntegerType(), - ] - ), - ), - ], - type=ast.SelectQueryType(aliases={}, columns={}, tables={}), - ) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_boolean_operation_types(self): expr = self._select("SELECT 1 and 1, 1 or 1, not true") expr = resolve_types(expr, self.context) - expected = ast.SelectQuery( - select=[ - ast.And( - exprs=[ - ast.Constant(value=1, type=ast.IntegerType()), - ast.Constant(value=1, type=ast.IntegerType()), - ], - type=ast.BooleanType(), - ), - ast.Or( - exprs=[ - ast.Constant(value=1, type=ast.IntegerType()), - ast.Constant(value=1, type=ast.IntegerType()), - ], - type=ast.BooleanType(), - ), - ast.Not( - expr=ast.Constant(value=True, type=ast.BooleanType()), - type=ast.BooleanType(), - ), - ], - type=ast.SelectQueryType(aliases={}, columns={}, tables={}), - ) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot def test_resolve_errors(self): queries = [ @@ -369,388 +123,53 @@ def test_resolve_errors(self): resolve_types(self._select(query), self.context) self.assertIn("Unable to resolve field:", str(e.exception)) + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_lazy_pdi_person_table(self): expr = self._select("select distinct_id, person.id from person_distinct_ids") expr = resolve_types(expr, self.context) - pdi_table_type = ast.LazyTableType(table=self.database.person_distinct_ids) - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["distinct_id"], - type=ast.FieldType(name="distinct_id", table_type=pdi_table_type), - ), - ast.Field( - chain=["person", "id"], - type=ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=pdi_table_type, - field="person", - lazy_join=self.database.person_distinct_ids.fields.get("person"), - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["person_distinct_ids"], type=pdi_table_type), - type=pdi_table_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "distinct_id": ast.FieldType(name="distinct_id", table_type=pdi_table_type), - "id": ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=pdi_table_type, - lazy_join=self.database.person_distinct_ids.fields.get("person"), - field="person", - ), - ), - }, - tables={"person_distinct_ids": pdi_table_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_lazy_events_pdi_table(self): expr = self._select("select event, pdi.person_id from events") expr = resolve_types(expr, self.context) - events_table_type = ast.TableType(table=self.database.events) - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["pdi", "person_id"], - type=ast.FieldType( - name="person_id", - table_type=ast.LazyJoinType( - table_type=events_table_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - type=events_table_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "event": ast.FieldType(name="event", table_type=events_table_type), - "person_id": ast.FieldType( - name="person_id", - table_type=ast.LazyJoinType( - table_type=events_table_type, - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - field="pdi", - ), - ), - }, - tables={"events": events_table_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_lazy_events_pdi_table_aliased(self): expr = self._select("select event, e.pdi.person_id from events e") expr = resolve_types(expr, self.context) - events_table_type = ast.TableType(table=self.database.events) - events_table_alias_type = ast.TableAliasType(table_type=events_table_type, alias="e") - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_alias_type), - ), - ast.Field( - chain=["e", "pdi", "person_id"], - type=ast.FieldType( - name="person_id", - table_type=ast.LazyJoinType( - table_type=events_table_alias_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - alias="e", - type=events_table_alias_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "event": ast.FieldType(name="event", table_type=events_table_alias_type), - "person_id": ast.FieldType( - name="person_id", - table_type=ast.LazyJoinType( - table_type=events_table_alias_type, - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - field="pdi", - ), - ), - }, - tables={"e": events_table_alias_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_lazy_events_pdi_person_table(self): expr = self._select("select event, pdi.person.id from events") expr = resolve_types(expr, self.context) - events_table_type = ast.TableType(table=self.database.events) - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["pdi", "person", "id"], - type=ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=ast.LazyJoinType( - table_type=events_table_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - field="person", - lazy_join=cast( - LazyJoin, - cast(LazyJoin, self.database.events.fields.get("pdi")).join_table.fields.get("person"), - ), - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - type=events_table_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "event": ast.FieldType(name="event", table_type=events_table_type), - "id": ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=ast.LazyJoinType( - table_type=events_table_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - field="person", - lazy_join=cast( - LazyJoin, - cast(LazyJoin, self.database.events.fields.get("pdi")).join_table.fields.get("person"), - ), - ), - ), - }, - tables={"events": events_table_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_lazy_events_pdi_person_table_aliased(self): expr = self._select("select event, e.pdi.person.id from events e") expr = resolve_types(expr, self.context) - events_table_type = ast.TableType(table=self.database.events) - events_table_alias_type = ast.TableAliasType(table_type=events_table_type, alias="e") - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_alias_type), - ), - ast.Field( - chain=["e", "pdi", "person", "id"], - type=ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=ast.LazyJoinType( - table_type=events_table_alias_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - field="person", - lazy_join=cast( - LazyJoin, - cast(LazyJoin, self.database.events.fields.get("pdi")).join_table.fields.get("person"), - ), - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - alias="e", - type=events_table_alias_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "event": ast.FieldType(name="event", table_type=events_table_alias_type), - "id": ast.FieldType( - name="id", - table_type=ast.LazyJoinType( - table_type=ast.LazyJoinType( - table_type=events_table_alias_type, - field="pdi", - lazy_join=cast(LazyJoin, self.database.events.fields.get("pdi")), - ), - field="person", - lazy_join=cast( - LazyJoin, - cast(LazyJoin, self.database.events.fields.get("pdi")).join_table.fields.get("person"), - ), - ), - ), - }, - tables={"e": events_table_alias_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_virtual_events_poe(self): expr = self._select("select event, poe.id from events") expr = resolve_types(expr, self.context) - events_table_type = ast.TableType(table=self.database.events) - expected = ast.SelectQuery( - select=[ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["poe", "id"], - type=ast.FieldType( - name="id", - table_type=ast.VirtualTableType( - table_type=events_table_type, - field="poe", - virtual_table=self.database.events.fields["poe"], - ), - ), - ), - ], - select_from=ast.JoinExpr( - table=ast.Field(chain=["events"], type=events_table_type), - type=events_table_type, - ), - type=ast.SelectQueryType( - aliases={}, - anonymous_tables=[], - columns={ - "event": ast.FieldType(name="event", table_type=events_table_type), - "id": ast.FieldType( - name="id", - table_type=ast.VirtualTableType( - table_type=events_table_type, - field="poe", - virtual_table=self.database.events.fields.get("poe"), - ), - ), - }, - tables={"events": events_table_type}, - ), - ) - self.assertEqual(expr.select, expected.select) - self.assertEqual(expr.select_from, expected.select_from) - self.assertEqual(expr.where, expected.where) - self.assertEqual(expr.type, expected.type) - self.assertEqual(expr, expected) + assert pretty_dataclasses(expr) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_resolve_union_all(self): node = self._select("select event, timestamp from events union all select event, timestamp from events") node = resolve_types(node, self.context) + assert pretty_dataclasses(node) == self.snapshot - events_table_type = ast.TableType(table=self.database.events) - self.assertEqual( - node.select_queries[0].select, - [ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=events_table_type), - ), - ], - ) - self.assertEqual( - node.select_queries[1].select, - [ - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=events_table_type), - ), - ], - ) - + @pytest.mark.usefixtures("unittest_snapshot") def test_call_type(self): node = self._select("select max(timestamp) from events") node = resolve_types(node, self.context) - expected = [ - ast.Call( - name="max", - # NB! timestamp was resolved to a DateTimeType for the Call's arg type. - type=ast.CallType( - name="max", - arg_types=[ast.DateTimeType()], - return_type=ast.UnknownType(), - ), - args=[ - ast.Field( - chain=["timestamp"], - type=ast.FieldType( - name="timestamp", - table_type=ast.TableType(table=self.database.events), - ), - ) - ], - ), - ] - self.assertEqual(node.select, expected) + assert pretty_dataclasses(node) == self.snapshot def test_ctes_loop(self): with self.assertRaises(ResolverException) as e: @@ -760,10 +179,7 @@ def test_ctes_loop(self): def test_ctes_basic_column(self): expr = self._print_hogql("with 1 as cte select cte from events") expected = self._print_hogql("select 1 from events") - self.assertEqual( - expr, - expected, - ) + self.assertEqual(expr, expected) def test_ctes_recursive_column(self): self.assertEqual( @@ -815,282 +231,40 @@ def test_ctes_subquery_recursion(self): ) @override_settings(PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False) + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_table(self): self.setUp() # rebuild self.database with PERSON_ON_EVENTS_OVERRIDE=False node = self._select("select * from events") node = resolve_types(node, self.context) - - events_table_type = ast.TableType(table=self.database.events) - self.assertEqual( - node.select, - [ - ast.Field( - chain=["uuid"], - type=ast.FieldType(name="uuid", table_type=events_table_type), - ), - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_type), - ), - ast.Field( - chain=["properties"], - type=ast.FieldType(name="properties", table_type=events_table_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=events_table_type), - ), - ast.Field( - chain=["distinct_id"], - type=ast.FieldType(name="distinct_id", table_type=events_table_type), - ), - ast.Field( - chain=["elements_chain"], - type=ast.FieldType(name="elements_chain", table_type=events_table_type), - ), - ast.Field( - chain=["created_at"], - type=ast.FieldType(name="created_at", table_type=events_table_type), - ), - ast.Field( - chain=["$session_id"], - type=ast.FieldType(name="$session_id", table_type=events_table_type), - ), - ast.Field( - chain=["$group_0"], - type=ast.FieldType(name="$group_0", table_type=events_table_type), - ), - ast.Field( - chain=["$group_1"], - type=ast.FieldType(name="$group_1", table_type=events_table_type), - ), - ast.Field( - chain=["$group_2"], - type=ast.FieldType(name="$group_2", table_type=events_table_type), - ), - ast.Field( - chain=["$group_3"], - type=ast.FieldType(name="$group_3", table_type=events_table_type), - ), - ast.Field( - chain=["$group_4"], - type=ast.FieldType(name="$group_4", table_type=events_table_type), - ), - ], - ) + assert pretty_dataclasses(node) == self.snapshot @override_settings(PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False) + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_table_alias(self): self.setUp() # rebuild self.database with PERSON_ON_EVENTS_OVERRIDE=False node = self._select("select * from events e") node = resolve_types(node, self.context) + assert pretty_dataclasses(node) == self.snapshot - events_table_type = ast.TableType(table=self.database.events) - events_table_alias_type = ast.TableAliasType(table_type=events_table_type, alias="e") - self.assertEqual( - node.select, - [ - ast.Field( - chain=["uuid"], - type=ast.FieldType(name="uuid", table_type=events_table_alias_type), - ), - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=events_table_alias_type), - ), - ast.Field( - chain=["properties"], - type=ast.FieldType(name="properties", table_type=events_table_alias_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=events_table_alias_type), - ), - ast.Field( - chain=["distinct_id"], - type=ast.FieldType(name="distinct_id", table_type=events_table_alias_type), - ), - ast.Field( - chain=["elements_chain"], - type=ast.FieldType(name="elements_chain", table_type=events_table_alias_type), - ), - ast.Field( - chain=["created_at"], - type=ast.FieldType(name="created_at", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$session_id"], - type=ast.FieldType(name="$session_id", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$group_0"], - type=ast.FieldType(name="$group_0", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$group_1"], - type=ast.FieldType(name="$group_1", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$group_2"], - type=ast.FieldType(name="$group_2", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$group_3"], - type=ast.FieldType(name="$group_3", table_type=events_table_alias_type), - ), - ast.Field( - chain=["$group_4"], - type=ast.FieldType(name="$group_4", table_type=events_table_alias_type), - ), - ], - ) - + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_subquery(self): node = self._select("select * from (select 1 as a, 2 as b)") node = resolve_types(node, self.context) - select_subquery_type = ast.SelectQueryType( - aliases={ - "a": ast.FieldAliasType(alias="a", type=ast.IntegerType()), - "b": ast.FieldAliasType(alias="b", type=ast.IntegerType()), - }, - columns={ - "a": ast.FieldAliasType(alias="a", type=ast.IntegerType()), - "b": ast.FieldAliasType(alias="b", type=ast.IntegerType()), - }, - tables={}, - anonymous_tables=[], - ) - self.assertEqual( - node.select, - [ - ast.Field( - chain=["a"], - type=ast.FieldType(name="a", table_type=select_subquery_type), - ), - ast.Field( - chain=["b"], - type=ast.FieldType(name="b", table_type=select_subquery_type), - ), - ], - ) + assert pretty_dataclasses(node) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_subquery_alias(self): node = self._select("select x.* from (select 1 as a, 2 as b) x") node = resolve_types(node, self.context) - select_subquery_type = ast.SelectQueryAliasType( - alias="x", - select_query_type=ast.SelectQueryType( - aliases={ - "a": ast.FieldAliasType(alias="a", type=ast.IntegerType()), - "b": ast.FieldAliasType(alias="b", type=ast.IntegerType()), - }, - columns={ - "a": ast.FieldAliasType(alias="a", type=ast.IntegerType()), - "b": ast.FieldAliasType(alias="b", type=ast.IntegerType()), - }, - tables={}, - anonymous_tables=[], - ), - ) - self.assertEqual( - node.select, - [ - ast.Field( - chain=["a"], - type=ast.FieldType(name="a", table_type=select_subquery_type), - ), - ast.Field( - chain=["b"], - type=ast.FieldType(name="b", table_type=select_subquery_type), - ), - ], - ) + assert pretty_dataclasses(node) == self.snapshot @override_settings(PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False) + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_from_subquery_table(self): self.setUp() # rebuild self.database with PERSON_ON_EVENTS_OVERRIDE=False node = self._select("select * from (select * from events)") node = resolve_types(node, self.context) - - events_table_type = ast.TableType(table=self.database.events) - inner_select_type = ast.SelectQueryType( - tables={"events": events_table_type}, - anonymous_tables=[], - aliases={}, - columns={ - "uuid": ast.FieldType(name="uuid", table_type=events_table_type), - "event": ast.FieldType(name="event", table_type=events_table_type), - "properties": ast.FieldType(name="properties", table_type=events_table_type), - "timestamp": ast.FieldType(name="timestamp", table_type=events_table_type), - "distinct_id": ast.FieldType(name="distinct_id", table_type=events_table_type), - "elements_chain": ast.FieldType(name="elements_chain", table_type=events_table_type), - "created_at": ast.FieldType(name="created_at", table_type=events_table_type), - "$session_id": ast.FieldType(name="$session_id", table_type=events_table_type), - "$group_0": ast.FieldType(name="$group_0", table_type=events_table_type), - "$group_1": ast.FieldType(name="$group_1", table_type=events_table_type), - "$group_2": ast.FieldType(name="$group_2", table_type=events_table_type), - "$group_3": ast.FieldType(name="$group_3", table_type=events_table_type), - "$group_4": ast.FieldType(name="$group_4", table_type=events_table_type), - }, - ) - - self.assertEqual( - node.select, - [ - ast.Field( - chain=["uuid"], - type=ast.FieldType(name="uuid", table_type=inner_select_type), - ), - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=inner_select_type), - ), - ast.Field( - chain=["properties"], - type=ast.FieldType(name="properties", table_type=inner_select_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=inner_select_type), - ), - ast.Field( - chain=["distinct_id"], - type=ast.FieldType(name="distinct_id", table_type=inner_select_type), - ), - ast.Field( - chain=["elements_chain"], - type=ast.FieldType(name="elements_chain", table_type=inner_select_type), - ), - ast.Field( - chain=["created_at"], - type=ast.FieldType(name="created_at", table_type=inner_select_type), - ), - ast.Field( - chain=["$session_id"], - type=ast.FieldType(name="$session_id", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_0"], - type=ast.FieldType(name="$group_0", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_1"], - type=ast.FieldType(name="$group_1", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_2"], - type=ast.FieldType(name="$group_2", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_3"], - type=ast.FieldType(name="$group_3", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_4"], - type=ast.FieldType(name="$group_4", table_type=inner_select_type), - ), - ], - ) + assert pretty_dataclasses(node) == self.snapshot def test_asterisk_expander_multiple_table_error(self): node = self._select("select * from (select 1 as a, 2 as b) x left join (select 1 as a, 2 as b) y on x.a = y.a") @@ -1102,95 +276,12 @@ def test_asterisk_expander_multiple_table_error(self): ) @override_settings(PERSON_ON_EVENTS_OVERRIDE=False, PERSON_ON_EVENTS_V2_OVERRIDE=False) + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_select_union(self): self.setUp() # rebuild self.database with PERSON_ON_EVENTS_OVERRIDE=False node = self._select("select * from (select * from events union all select * from events)") node = resolve_types(node, self.context) - - events_table_type = ast.TableType(table=self.database.events) - inner_select_type = ast.SelectUnionQueryType( - types=[ - ast.SelectQueryType( - tables={"events": events_table_type}, - anonymous_tables=[], - aliases={}, - columns={ - "uuid": ast.FieldType(name="uuid", table_type=events_table_type), - "event": ast.FieldType(name="event", table_type=events_table_type), - "properties": ast.FieldType(name="properties", table_type=events_table_type), - "timestamp": ast.FieldType(name="timestamp", table_type=events_table_type), - "distinct_id": ast.FieldType(name="distinct_id", table_type=events_table_type), - "elements_chain": ast.FieldType(name="elements_chain", table_type=events_table_type), - "created_at": ast.FieldType(name="created_at", table_type=events_table_type), - "$session_id": ast.FieldType(name="$session_id", table_type=events_table_type), - "$group_0": ast.FieldType(name="$group_0", table_type=events_table_type), - "$group_1": ast.FieldType(name="$group_1", table_type=events_table_type), - "$group_2": ast.FieldType(name="$group_2", table_type=events_table_type), - "$group_3": ast.FieldType(name="$group_3", table_type=events_table_type), - "$group_4": ast.FieldType(name="$group_4", table_type=events_table_type), - }, - ) - ] - * 2 - ) - - self.assertEqual( - node.select, - [ - ast.Field( - chain=["uuid"], - type=ast.FieldType(name="uuid", table_type=inner_select_type), - ), - ast.Field( - chain=["event"], - type=ast.FieldType(name="event", table_type=inner_select_type), - ), - ast.Field( - chain=["properties"], - type=ast.FieldType(name="properties", table_type=inner_select_type), - ), - ast.Field( - chain=["timestamp"], - type=ast.FieldType(name="timestamp", table_type=inner_select_type), - ), - ast.Field( - chain=["distinct_id"], - type=ast.FieldType(name="distinct_id", table_type=inner_select_type), - ), - ast.Field( - chain=["elements_chain"], - type=ast.FieldType(name="elements_chain", table_type=inner_select_type), - ), - ast.Field( - chain=["created_at"], - type=ast.FieldType(name="created_at", table_type=inner_select_type), - ), - ast.Field( - chain=["$session_id"], - type=ast.FieldType(name="$session_id", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_0"], - type=ast.FieldType(name="$group_0", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_1"], - type=ast.FieldType(name="$group_1", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_2"], - type=ast.FieldType(name="$group_2", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_3"], - type=ast.FieldType(name="$group_3", table_type=inner_select_type), - ), - ast.Field( - chain=["$group_4"], - type=ast.FieldType(name="$group_4", table_type=inner_select_type), - ), - ], - ) + assert pretty_dataclasses(node) == self.snapshot def test_lambda_parent_scope(self): # does not raise @@ -1234,7 +325,8 @@ def test_visit_hogqlx_tag(self): node = cast(ast.SelectQuery, resolve_types(node, self.context)) table_node = cast(ast.SelectQuery, node).select_from.table expected = ast.SelectQuery( - select=[ast.Field(chain=["event"])], select_from=ast.JoinExpr(table=ast.Field(chain=["events"])) + select=[ast.Field(chain=["event"])], + select_from=ast.JoinExpr(table=ast.Field(chain=["events"])), ) assert clone_expr(table_node, clear_types=True) == expected diff --git a/posthog/hogql/test/utils.py b/posthog/hogql/test/utils.py index 8e5fc45313a0f..7e46c620c997a 100644 --- a/posthog/hogql/test/utils.py +++ b/posthog/hogql/test/utils.py @@ -1,3 +1,8 @@ +import dataclasses +import json +from pydantic import BaseModel + + def pretty_print_in_tests(query: str, team_id: int) -> str: return ( query.replace("SELECT", "\nSELECT") @@ -9,3 +14,52 @@ def pretty_print_in_tests(query: str, team_id: int) -> str: .replace("SETTINGS", "\nSETTINGS") .replace(f"team_id, {team_id})", "team_id, 420)") ) + + +def pretty_dataclasses(obj, seen=None, indent=0): + if seen is None: + seen = set() + + indent_space = " " * indent + next_indent = " " * (indent + 2) + + if isinstance(obj, BaseModel): + obj = obj.model_dump() + + if dataclasses.is_dataclass(obj): + obj_id = id(obj) + if obj_id in seen: + return "" + seen.add(obj_id) + + field_strings = [] + fields = sorted(dataclasses.fields(obj), key=lambda f: f.name) + for f in fields: + value = getattr(obj, f.name) + if value is not None: + formatted_value = pretty_dataclasses(value, seen, indent + 2) + field_strings.append(f"{next_indent}{f.name}: {formatted_value}") + + return "{\n" + "\n".join(field_strings) + "\n" + indent_space + "}" + + elif isinstance(obj, list): + if len(obj) == 0: + return "[]" + elements = [pretty_dataclasses(item, seen, indent + 2) for item in obj] + return "[\n" + ",\n".join(next_indent + element for element in elements) + "\n" + indent_space + "]" + + elif isinstance(obj, dict): + if len(obj) == 0: + return "{}" + sorted_items = sorted(obj.items()) + key_value_pairs = [f"{k}: {pretty_dataclasses(v, seen, indent + 2)}" for k, v in sorted_items] + return "{\n" + ",\n".join(next_indent + pair for pair in key_value_pairs) + "\n" + indent_space + "}" + + elif isinstance(obj, str): + return json.dumps(obj) + + elif callable(obj): + return "" + + else: + return str(obj) diff --git a/posthog/migrations/0365_update_created_by_flag_constraint.py b/posthog/migrations/0365_update_created_by_flag_constraint.py new file mode 100644 index 0000000000000..e8912598ae3fd --- /dev/null +++ b/posthog/migrations/0365_update_created_by_flag_constraint.py @@ -0,0 +1,85 @@ +# Generated by Django 3.2.19 on 2023-11-09 10:35 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0364_team_external_data_workspace_rows"), + ] + + # :TRICKY: + # We are replacing the original generated migration: + # migrations.AlterField( + # model_name='experiment', + # name='created_by', + # field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + # ), + # migrations.AlterField( + # model_name='featureflag', + # name='created_by', + # field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + # ), + # with one that adds the 'NOT VALID' directive, which applies the constraint only for inserts/updates. + # This ensures the table is not locked when creating the new constraint. + # A follow up migration will validate the constraint. + # The code here is exactly the same as the one generated by the default migration, except for the 'NOT VALID' directive. + + operations = [ + # make the created_by column nullable in experiments & flags + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.AlterField( + model_name="experiment", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="posthog.user", + ), + ), + migrations.AlterField( + model_name="featureflag", + name="created_by", + field=models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="posthog.user", + ), + ), + ], + database_operations=[ + # We add -- existing-table-constraint-ignore to ignore the constraint validation in CI. + # This should be safe, because we are making the constraint NOT VALID, so doesn't lock things up for long. + migrations.RunSQL( + """ + SET CONSTRAINTS "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id" IMMEDIATE; -- existing-table-constraint-ignore + ALTER TABLE "posthog_experiment" DROP CONSTRAINT "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id"; -- existing-table-constraint-ignore + ALTER TABLE "posthog_experiment" ALTER COLUMN "created_by_id" DROP NOT NULL; + ALTER TABLE "posthog_experiment" ADD CONSTRAINT "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id" FOREIGN KEY ("created_by_id") REFERENCES "posthog_user" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID; -- existing-table-constraint-ignore + """, + reverse_sql=""" + SET CONSTRAINTS "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id" IMMEDIATE; + ALTER TABLE "posthog_experiment" DROP CONSTRAINT "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id"; + ALTER TABLE "posthog_experiment" ALTER COLUMN "created_by_id" SET NOT NULL; + ALTER TABLE "posthog_experiment" ADD CONSTRAINT "posthog_experiment_created_by_id_b40aea95_fk_posthog_user_id" FOREIGN KEY ("created_by_id") REFERENCES "posthog_user" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID; + """, + ), + migrations.RunSQL( + """SET CONSTRAINTS "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id" IMMEDIATE; -- existing-table-constraint-ignore + ALTER TABLE "posthog_featureflag" DROP CONSTRAINT "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id"; -- existing-table-constraint-ignore + ALTER TABLE "posthog_featureflag" ALTER COLUMN "created_by_id" DROP NOT NULL; + ALTER TABLE "posthog_featureflag" ADD CONSTRAINT "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id" FOREIGN KEY ("created_by_id") REFERENCES "posthog_user" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID; -- existing-table-constraint-ignore + """, + reverse_sql=""" + SET CONSTRAINTS "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id" IMMEDIATE; + ALTER TABLE "posthog_featureflag" DROP CONSTRAINT "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id"; + ALTER TABLE "posthog_featureflag" ALTER COLUMN "created_by_id" SET NOT NULL; + ALTER TABLE "posthog_featureflag" ADD CONSTRAINT "posthog_featureflag_created_by_id_4571fe1a_fk_posthog_user_id" FOREIGN KEY ("created_by_id") REFERENCES "posthog_user" ("id") DEFERRABLE INITIALLY DEFERRED NOT VALID; + -- existing-table-constraint-ignore + """, + ), + ], + ), + ] diff --git a/posthog/migrations/0366_alter_action_created_by.py b/posthog/migrations/0366_alter_action_created_by.py new file mode 100644 index 0000000000000..996183b7625bf --- /dev/null +++ b/posthog/migrations/0366_alter_action_created_by.py @@ -0,0 +1,21 @@ +# Generated by Django 3.2.19 on 2023-11-21 14:02 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("posthog", "0365_update_created_by_flag_constraint"), + ] + + operations = [ + migrations.AlterField( + model_name="action", + name="created_by", + field=models.ForeignKey( + blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/posthog/models/action/action.py b/posthog/models/action/action.py index 368100fcbc978..698957f8dbafd 100644 --- a/posthog/models/action/action.py +++ b/posthog/models/action/action.py @@ -20,7 +20,7 @@ class Meta: team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) description: models.TextField = models.TextField(blank=True, default="") created_at: models.DateTimeField = models.DateTimeField(auto_now_add=True, blank=True) - created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.CASCADE, null=True, blank=True) + created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.SET_NULL, null=True, blank=True) deleted: models.BooleanField = models.BooleanField(default=False) events: models.ManyToManyField = models.ManyToManyField("Event", blank=True) post_to_slack: models.BooleanField = models.BooleanField(default=False) diff --git a/posthog/models/cohort/cohort.py b/posthog/models/cohort/cohort.py index b907df41c934a..9310900e0f8d0 100644 --- a/posthog/models/cohort/cohort.py +++ b/posthog/models/cohort/cohort.py @@ -238,7 +238,7 @@ def calculate_people_ch(self, pending_version): def insert_users_by_list(self, items: List[str]) -> None: """ - Items can be distinct_id or email + Items is a list of distinct_ids """ batchsize = 1000 @@ -298,9 +298,8 @@ def insert_users_by_list(self, items: List[str]) -> None: self.save() capture_exception(err) - def insert_users_list_by_uuid(self, items: List[str]) -> None: - batchsize = 1000 - from posthog.models.cohort.util import get_static_cohort_size + def insert_users_list_by_uuid(self, items: List[str], insert_in_clickhouse: bool = False, batchsize=1000) -> None: + from posthog.models.cohort.util import get_static_cohort_size, insert_static_cohort try: cursor = connection.cursor() @@ -309,6 +308,12 @@ def insert_users_list_by_uuid(self, items: List[str]) -> None: persons_query = ( Person.objects.filter(team_id=self.team_id).filter(uuid__in=batch).exclude(cohort__id=self.id) ) + if insert_in_clickhouse: + insert_static_cohort( + [p for p in persons_query.values_list("uuid", flat=True)], + self.pk, + self.team, + ) sql, params = persons_query.distinct("pk").only("pk").query.sql_with_params() query = UPDATE_QUERY.format( cohort_id=self.pk, diff --git a/posthog/models/cohort/test/test_util.py b/posthog/models/cohort/test/test_util.py index d8ff051a0bb41..dce0258746828 100644 --- a/posthog/models/cohort/test/test_util.py +++ b/posthog/models/cohort/test/test_util.py @@ -508,3 +508,39 @@ def test_dependent_cohorts_for_complex_nested_cohort(self): self.assertEqual(get_dependent_cohorts(cohort3), [cohort2, cohort1]) self.assertEqual(get_dependent_cohorts(cohort4), [cohort1]) self.assertEqual(get_dependent_cohorts(cohort5), [cohort4, cohort1, cohort2]) + + def test_dependent_cohorts_ignore_invalid_ids(self): + cohort1 = _create_cohort( + team=self.team, + name="cohort1", + groups=[{"properties": [{"key": "name", "value": "test", "type": "person"}]}], + ) + + cohort2 = _create_cohort( + team=self.team, + name="cohort2", + groups=[ + { + "properties": [ + {"key": "id", "value": cohort1.pk, "type": "cohort"}, + {"key": "id", "value": "invalid-key", "type": "cohort"}, + ] + } + ], + ) + + cohort3 = _create_cohort( + team=self.team, + name="cohorte", + groups=[ + { + "properties": [ + {"key": "id", "value": cohort2.pk, "type": "cohort"}, + {"key": "id", "value": "invalid-key", "type": "cohort"}, + ] + } + ], + ) + + self.assertEqual(get_dependent_cohorts(cohort2), [cohort1]) + self.assertEqual(get_dependent_cohorts(cohort3), [cohort2, cohort1]) diff --git a/posthog/models/cohort/util.py b/posthog/models/cohort/util.py index abd4e6c89920c..c4201fbaf3f47 100644 --- a/posthog/models/cohort/util.py +++ b/posthog/models/cohort/util.py @@ -440,7 +440,7 @@ def get_all_cohort_ids_by_person_uuid(uuid: str, team_id: int) -> List[int]: def get_dependent_cohorts( cohort: Cohort, using_database: str = "default", - seen_cohorts_cache: Optional[Dict[str, Cohort]] = None, + seen_cohorts_cache: Optional[Dict[int, Cohort]] = None, ) -> List[Cohort]: if seen_cohorts_cache is None: seen_cohorts_cache = {} @@ -449,28 +449,40 @@ def get_dependent_cohorts( seen_cohort_ids = set() seen_cohort_ids.add(cohort.id) - queue = [prop.value for prop in cohort.properties.flat if prop.type == "cohort"] + queue = [] + for prop in cohort.properties.flat: + if prop.type == "cohort" and not isinstance(prop.value, list): + try: + queue.append(int(prop.value)) + except (ValueError, TypeError): + continue while queue: cohort_id = queue.pop() try: - parsed_cohort_id = str(cohort_id) - if parsed_cohort_id in seen_cohorts_cache: - cohort = seen_cohorts_cache[parsed_cohort_id] + if cohort_id in seen_cohorts_cache: + cohort = seen_cohorts_cache[cohort_id] else: cohort = Cohort.objects.using(using_database).get(pk=cohort_id) - seen_cohorts_cache[parsed_cohort_id] = cohort + seen_cohorts_cache[cohort_id] = cohort if cohort.id not in seen_cohort_ids: cohorts.append(cohort) seen_cohort_ids.add(cohort.id) - queue += [prop.value for prop in cohort.properties.flat if prop.type == "cohort"] + + for prop in cohort.properties.flat: + if prop.type == "cohort" and not isinstance(prop.value, list): + try: + queue.append(int(prop.value)) + except (ValueError, TypeError): + continue + except Cohort.DoesNotExist: continue return cohorts -def sort_cohorts_topologically(cohort_ids: Set[int], seen_cohorts_cache: Dict[str, Cohort]) -> List[int]: +def sort_cohorts_topologically(cohort_ids: Set[int], seen_cohorts_cache: Dict[int, Cohort]) -> List[int]: """ Sorts the given cohorts in an order where cohorts with no dependencies are placed first, followed by cohorts that depend on the preceding ones. It ensures that each cohort in the sorted list @@ -492,13 +504,13 @@ def traverse(cohort): # add child dependency_graph[cohort.id].append(int(prop.value)) - neighbor_cohort = seen_cohorts_cache[str(prop.value)] + neighbor_cohort = seen_cohorts_cache[int(prop.value)] if cohort.id not in seen: seen.add(cohort.id) traverse(neighbor_cohort) for cohort_id in cohort_ids: - cohort = seen_cohorts_cache[str(cohort_id)] + cohort = seen_cohorts_cache[int(cohort_id)] traverse(cohort) # post-order DFS (children first, then the parent) diff --git a/posthog/models/experiment.py b/posthog/models/experiment.py index ea970c5b2db12..74e631b6fab8c 100644 --- a/posthog/models/experiment.py +++ b/posthog/models/experiment.py @@ -23,8 +23,8 @@ class Experiment(models.Model): # A list of filters for secondary metrics secondary_metrics: models.JSONField = models.JSONField(default=list, null=True) + created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.SET_NULL, null=True) feature_flag: models.ForeignKey = models.ForeignKey("FeatureFlag", blank=False, on_delete=models.RESTRICT) - created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.CASCADE) start_date: models.DateTimeField = models.DateTimeField(null=True) end_date: models.DateTimeField = models.DateTimeField(null=True) created_at: models.DateTimeField = models.DateTimeField(default=timezone.now) diff --git a/posthog/models/feature_flag/feature_flag.py b/posthog/models/feature_flag/feature_flag.py index 36379563aa7f7..c339abe44d0ed 100644 --- a/posthog/models/feature_flag/feature_flag.py +++ b/posthog/models/feature_flag/feature_flag.py @@ -37,7 +37,7 @@ class Meta: rollout_percentage: models.IntegerField = models.IntegerField(null=True, blank=True) team: models.ForeignKey = models.ForeignKey("Team", on_delete=models.CASCADE) - created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.CASCADE) + created_by: models.ForeignKey = models.ForeignKey("User", on_delete=models.SET_NULL, null=True) created_at: models.DateTimeField = models.DateTimeField(default=timezone.now) deleted: models.BooleanField = models.BooleanField(default=False) active: models.BooleanField = models.BooleanField(default=True) @@ -134,7 +134,7 @@ def get_filters(self): def transform_cohort_filters_for_easy_evaluation( self, using_database: str = "default", - seen_cohorts_cache: Optional[Dict[str, Cohort]] = None, + seen_cohorts_cache: Optional[Dict[int, Cohort]] = None, ): """ Expands cohort filters into person property filters when possible. @@ -168,18 +168,17 @@ def transform_cohort_filters_for_easy_evaluation( for prop in props: if prop.get("type") == "cohort": cohort_condition = True - cohort_id = prop.get("value") + cohort_id = int(prop.get("value")) if cohort_id: if len(props) > 1: # We cannot expand this cohort condition if it's not the only property in its group. return self.conditions try: - parsed_cohort_id = str(cohort_id) - if parsed_cohort_id in seen_cohorts_cache: - cohort = seen_cohorts_cache[parsed_cohort_id] + if cohort_id in seen_cohorts_cache: + cohort = seen_cohorts_cache[cohort_id] else: cohort = Cohort.objects.using(using_database).get(pk=cohort_id) - seen_cohorts_cache[parsed_cohort_id] = cohort + seen_cohorts_cache[cohort_id] = cohort except Cohort.DoesNotExist: return self.conditions if not cohort_condition: @@ -259,7 +258,7 @@ def transform_cohort_filters_for_easy_evaluation( def get_cohort_ids( self, using_database: str = "default", - seen_cohorts_cache: Optional[Dict[str, Cohort]] = None, + seen_cohorts_cache: Optional[Dict[int, Cohort]] = None, sort_by_topological_order=False, ) -> List[int]: from posthog.models.cohort.util import get_dependent_cohorts, sort_cohorts_topologically @@ -272,14 +271,13 @@ def get_cohort_ids( props = condition.get("properties", []) for prop in props: if prop.get("type") == "cohort": - cohort_id = prop.get("value") + cohort_id = int(prop.get("value")) try: - parsed_cohort_id = str(cohort_id) - if parsed_cohort_id in seen_cohorts_cache: - cohort: Cohort = seen_cohorts_cache[parsed_cohort_id] + if cohort_id in seen_cohorts_cache: + cohort: Cohort = seen_cohorts_cache[cohort_id] else: cohort = Cohort.objects.using(using_database).get(pk=cohort_id) - seen_cohorts_cache[parsed_cohort_id] = cohort + seen_cohorts_cache[cohort_id] = cohort cohort_ids.add(cohort.pk) cohort_ids.update( diff --git a/posthog/models/feature_flag/flag_matching.py b/posthog/models/feature_flag/flag_matching.py index 9a2722b86fc59..d81f44bd61807 100644 --- a/posthog/models/feature_flag/flag_matching.py +++ b/posthog/models/feature_flag/flag_matching.py @@ -138,6 +138,7 @@ def __init__( property_value_overrides: Dict[str, Union[str, int]] = {}, group_property_value_overrides: Dict[str, Dict[str, Union[str, int]]] = {}, skip_database_flags: bool = False, + cohorts_cache: Optional[Dict[int, Cohort]] = None, ): self.feature_flags = feature_flags self.distinct_id = distinct_id @@ -147,7 +148,11 @@ def __init__( self.property_value_overrides = property_value_overrides self.group_property_value_overrides = group_property_value_overrides self.skip_database_flags = skip_database_flags - self.cohorts_cache: Dict[int, Cohort] = {} + + if cohorts_cache is None: + self.cohorts_cache = {} + else: + self.cohorts_cache = cohorts_cache def get_match(self, feature_flag: FeatureFlag) -> FeatureFlagMatch: # If aggregating flag by groups and relevant group type is not passed - flag is off! @@ -481,7 +486,8 @@ def condition_eval(key, condition): group_fields, ) - if any(feature_flag.uses_cohorts for feature_flag in self.feature_flags): + # only fetch all cohorts if not passed in any cached cohorts + if not self.cohorts_cache and any(feature_flag.uses_cohorts for feature_flag in self.feature_flags): all_cohorts = { cohort.pk: cohort for cohort in Cohort.objects.using(DATABASE_FOR_FLAG_MATCHING).filter( @@ -582,6 +588,10 @@ def can_compute_locally( self.cache.group_type_index_to_name[group_type_index], {} ) for property in properties: + # can't locally compute if property is a cohort + # need to atleast fetch the cohort + if property.type == "cohort": + return False if property.key not in target_properties: return False if property.operator == "is_not_set": @@ -602,19 +612,24 @@ def get_highest_priority_match_evaluation( def get_feature_flag_hash_key_overrides( - team_id: int, distinct_ids: List[str], using_database: str = "default" + team_id: int, + distinct_ids: List[str], + using_database: str = "default", + person_id_to_distinct_id_mapping: Optional[Dict[int, str]] = None, ) -> Dict[str, str]: feature_flag_to_key_overrides = {} # Priority to the first distinctID's values, to keep this function deterministic - person_and_distinct_ids = list( - PersonDistinctId.objects.using(using_database) - .filter(distinct_id__in=distinct_ids, team_id=team_id) - .values_list("person_id", "distinct_id") - ) - - person_id_to_distinct_id = {person_id: distinct_id for person_id, distinct_id in person_and_distinct_ids} + if not person_id_to_distinct_id_mapping: + person_and_distinct_ids = list( + PersonDistinctId.objects.using(using_database) + .filter(distinct_id__in=distinct_ids, team_id=team_id) + .values_list("person_id", "distinct_id") + ) + person_id_to_distinct_id = {person_id: distinct_id for person_id, distinct_id in person_and_distinct_ids} + else: + person_id_to_distinct_id = person_id_to_distinct_id_mapping person_ids = list(person_id_to_distinct_id.keys()) diff --git a/posthog/models/feature_flag/permissions.py b/posthog/models/feature_flag/permissions.py index 95d39636c4c07..8f766b4fccc60 100644 --- a/posthog/models/feature_flag/permissions.py +++ b/posthog/models/feature_flag/permissions.py @@ -12,7 +12,7 @@ def can_user_edit_feature_flag(request, feature_flag): else: if not request.user.organization.is_feature_available(AvailableFeature.ROLE_BASED_ACCESS): return True - if feature_flag.created_by == request.user: + if hasattr(feature_flag, "created_by") and feature_flag.created_by and feature_flag.created_by == request.user: return True if ( request.user.organization_memberships.get(organization=request.user.organization).level diff --git a/posthog/models/organization.py b/posthog/models/organization.py index 869ba9f0f6e75..461c5f777f568 100644 --- a/posthog/models/organization.py +++ b/posthog/models/organization.py @@ -24,12 +24,7 @@ from posthog.cloud_utils import is_cloud from posthog.constants import MAX_SLUG_LENGTH, AvailableFeature from posthog.email import is_email_available -from posthog.models.utils import ( - LowercaseSlugField, - UUIDModel, - create_with_slug, - sane_repr, -) +from posthog.models.utils import LowercaseSlugField, UUIDModel, create_with_slug, sane_repr from posthog.redis import get_client from posthog.utils import absolute_uri @@ -416,3 +411,19 @@ def ensure_organization_membership_consistency(sender, instance: OrganizationMem save_user = True if save_user: instance.user.save() + + +@receiver(models.signals.pre_save, sender=OrganizationMembership) +def organization_membership_saved(sender: Any, instance: OrganizationMembership, **kwargs: Any) -> None: + from posthog.event_usage import report_user_organization_membership_level_changed + + try: + old_instance = OrganizationMembership.objects.get(id=instance.id) + if old_instance.level != instance.level: + # the level has been changed + report_user_organization_membership_level_changed( + instance.user, instance.organization, instance.level, old_instance.level + ) + except OrganizationMembership.DoesNotExist: + # The instance is new, or we are setting up test data + pass diff --git a/posthog/models/team/util.py b/posthog/models/team/util.py index a540624dfbb48..a21b75ab80384 100644 --- a/posthog/models/team/util.py +++ b/posthog/models/team/util.py @@ -2,7 +2,7 @@ from typing import Any, List from posthog.temporal.common.client import sync_connect -from posthog.batch_exports.service import delete_schedule +from posthog.batch_exports.service import batch_export_delete_schedule from posthog.cache_utils import cache_for from posthog.models.async_migration import is_async_migration_complete @@ -44,7 +44,7 @@ def delete_batch_exports(team_ids: List[int]): batch_export.delete() batch_export.destination.delete() - delete_schedule(temporal, str(schedule_id)) + batch_export_delete_schedule(temporal, str(schedule_id)) can_enable_actor_on_events = False diff --git a/posthog/models/test/test_organization_model.py b/posthog/models/test/test_organization_model.py index f140dcc862f26..8c35602a64be5 100644 --- a/posthog/models/test/test_organization_model.py +++ b/posthog/models/test/test_organization_model.py @@ -1,8 +1,10 @@ from unittest import mock +from unittest.mock import patch from django.utils import timezone from posthog.models import Organization, OrganizationInvite, Plugin +from posthog.models.organization import OrganizationMembership from posthog.plugins.test.mock import mocked_plugin_requests_get from posthog.plugins.test.plugin_archives import HELLO_WORLD_PLUGIN_GITHUB_ZIP from posthog.test.base import BaseTest @@ -77,3 +79,28 @@ def test_update_available_features_ignored_if_usage_info_exists(self): new_org.usage = {"events": {"usage": 1000, "limit": None}} new_org.update_available_features() assert new_org.available_features == ["test1", "test2"] + + +class TestOrganizationMembership(BaseTest): + @patch("posthoganalytics.capture") + def test_event_sent_when_membership_level_changed( + self, + mock_capture, + ): + user = self._create_user("user1") + organization = Organization.objects.create(name="Test Org") + membership = OrganizationMembership.objects.create(user=user, organization=organization, level=1) + mock_capture.assert_not_called() + # change the level + membership.level = 15 + membership.save() + # check that the event was sent + mock_capture.assert_called_once_with( + user.distinct_id, + "membership level changed", + properties={ + "new_level": 15, + "previous_level": 1, + }, + groups=mock.ANY, + ) diff --git a/posthog/models/test/test_user_model.py b/posthog/models/test/test_user_model.py index fe26931522eac..9c07f36b16466 100644 --- a/posthog/models/test/test_user_model.py +++ b/posthog/models/test/test_user_model.py @@ -10,6 +10,7 @@ def test_create_user_with_distinct_id(self): self.assertNotEqual(user.distinct_id, None) def test_analytics_metadata(self): + self.maxDiff = None # One org, one team, anonymized organization, team, user = User.objects.bootstrap( organization_name="Test Org", @@ -32,6 +33,7 @@ def test_analytics_metadata(self): "team_member_count_all": 1, "completed_onboarding_once": False, "organization_id": str(organization.id), + "current_organization_membership_level": 15, "project_id": str(team.uuid), "project_setup_complete": False, "has_password_set": True, @@ -67,6 +69,7 @@ def test_analytics_metadata(self): "team_member_count_all": 2, "completed_onboarding_once": True, "organization_id": str(self.organization.id), + "current_organization_membership_level": 1, "project_id": str(self.team.uuid), "project_setup_complete": True, "has_password_set": True, diff --git a/posthog/models/user.py b/posthog/models/user.py index 423936747e2cc..b25c12776fb1b 100644 --- a/posthog/models/user.py +++ b/posthog/models/user.py @@ -1,14 +1,5 @@ from functools import cached_property -from typing import ( - Any, - Callable, - Dict, - List, - Optional, - Tuple, - Type, - TypedDict, -) +from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypedDict from django.contrib.auth.models import AbstractUser, BaseUserManager from django.db import models, transaction @@ -237,6 +228,8 @@ def join( # We don't need to check for ExplicitTeamMembership as none can exist for a completely new member self.current_team = organization.teams.order_by("id").filter(access_control=False).first() self.save() + if level == OrganizationMembership.Level.OWNER and not self.current_organization.customer_id: + self.update_billing_customer_email(organization) self.update_billing_distinct_ids(organization) return membership @@ -268,6 +261,12 @@ def update_billing_distinct_ids(self, organization: Organization) -> None: if is_cloud() and get_cached_instance_license() is not None: BillingManager(get_cached_instance_license()).update_billing_distinct_ids(organization) + def update_billing_customer_email(self, organization: Organization) -> None: + from ee.billing.billing_manager import BillingManager # avoid circular import + + if is_cloud() and get_cached_instance_license() is not None: + BillingManager(get_cached_instance_license()).update_billing_customer_email(organization) + def get_analytics_metadata(self): team_member_count_all: int = ( OrganizationMembership.objects.filter(organization__in=self.organizations.all()) @@ -276,6 +275,10 @@ def get_analytics_metadata(self): .count() ) + current_organization_membership = None + if self.organization: + current_organization_membership = self.organization.memberships.filter(user=self).first() + project_setup_complete = False if self.team and self.team.completed_snippet_onboarding and self.team.ingested_event: project_setup_complete = True @@ -294,6 +297,9 @@ def get_analytics_metadata(self): ).exists(), # has completed the onboarding at least for one project # properties dependent on current project / org below "organization_id": str(self.organization.id) if self.organization else None, + "current_organization_membership_level": current_organization_membership.level + if current_organization_membership + else None, "project_id": str(self.team.uuid) if self.team else None, "project_setup_complete": project_setup_complete, "joined_at": self.date_joined, diff --git a/posthog/schema.py b/posthog/schema.py index a2057f903768f..46d107122cd8e 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -571,6 +571,7 @@ class TrendsFilter(BaseModel): display: Optional[ChartDisplayType] = None formula: Optional[str] = None hidden_legend_indexes: Optional[List[float]] = None + show_labels_on_series: Optional[bool] = None show_legend: Optional[bool] = None show_percent_stack_view: Optional[bool] = None show_values_on_series: Optional[bool] = None @@ -589,6 +590,14 @@ class TrendsQueryResponse(BaseModel): timings: Optional[List[QueryTiming]] = None +class ActionsPie(BaseModel): + model_config = ConfigDict( + extra="forbid", + ) + disableHoverOffset: Optional[bool] = None + hideAggregation: Optional[bool] = None + + class RETENTION(BaseModel): model_config = ConfigDict( extra="forbid", @@ -602,6 +611,7 @@ class VizSpecificOptions(BaseModel): model_config = ConfigDict( extra="forbid", ) + ActionsPie: Optional[ActionsPie] = None RETENTION: Optional[RETENTION] = None diff --git a/posthog/tasks/calculate_cohort.py b/posthog/tasks/calculate_cohort.py index 1c4492071c78a..066469636dc37 100644 --- a/posthog/tasks/calculate_cohort.py +++ b/posthog/tasks/calculate_cohort.py @@ -71,3 +71,10 @@ def insert_cohort_from_insight_filter(cohort_id: int, filter_data: Dict[str, Any insert_cohort_actors_into_ch(cohort, filter_data) insert_cohort_people_into_pg(cohort=cohort) + + +@shared_task(ignore_result=True, max_retries=1) +def insert_cohort_from_feature_flag(cohort_id: int, flag_key: str, team_id: int) -> None: + from posthog.api.cohort import get_cohort_actors_for_feature_flag + + get_cohort_actors_for_feature_flag(cohort_id, flag_key, team_id) diff --git a/posthog/temporal/batch_exports/bigquery_batch_export.py b/posthog/temporal/batch_exports/bigquery_batch_export.py index a1bffff3cc288..f4055de5760e5 100644 --- a/posthog/temporal/batch_exports/bigquery_batch_export.py +++ b/posthog/temporal/batch_exports/bigquery_batch_export.py @@ -1,7 +1,8 @@ +import asyncio import contextlib +import dataclasses import datetime as dt import json -from dataclasses import dataclass from django.conf import settings from google.cloud import bigquery @@ -10,6 +11,10 @@ from temporalio.common import RetryPolicy from posthog.batch_exports.service import BigQueryBatchExportInputs +from posthog.temporal.common.utils import ( + HeartbeatDetails, + should_resume_from_activity_heartbeat, +) from posthog.temporal.batch_exports.base import PostHogWorkflow from posthog.temporal.batch_exports.batch_exports import ( BatchExportTemporaryFile, @@ -26,7 +31,7 @@ from posthog.temporal.batch_exports.metrics import get_bytes_exported_metric, get_rows_exported_metric -def load_jsonl_file_to_bigquery_table(jsonl_file, table, table_schema, bigquery_client): +async def load_jsonl_file_to_bigquery_table(jsonl_file, table, table_schema, bigquery_client): """Execute a COPY FROM query with given connection to copy contents of jsonl_file.""" job_config = bigquery.LoadJobConfig( source_format="NEWLINE_DELIMITED_JSON", @@ -34,10 +39,10 @@ def load_jsonl_file_to_bigquery_table(jsonl_file, table, table_schema, bigquery_ ) load_job = bigquery_client.load_table_from_file(jsonl_file, table, job_config=job_config, rewind=True) - load_job.result() + await asyncio.to_thread(load_job.result) -def create_table_in_bigquery( +async def create_table_in_bigquery( project_id: str, dataset_id: str, table_id: str, @@ -49,12 +54,19 @@ def create_table_in_bigquery( fully_qualified_name = f"{project_id}.{dataset_id}.{table_id}" table = bigquery.Table(fully_qualified_name, schema=table_schema) table.time_partitioning = bigquery.TimePartitioning(type_=bigquery.TimePartitioningType.DAY, field="timestamp") - table = bigquery_client.create_table(table, exists_ok=exists_ok) + table = await asyncio.to_thread(bigquery_client.create_table, table, exists_ok=exists_ok) return table -@dataclass +@dataclasses.dataclass +class BigQueryHeartbeatDetails(HeartbeatDetails): + """The BigQuery batch export details included in every heartbeat.""" + + pass + + +@dataclasses.dataclass class BigQueryInsertInputs: """Inputs for BigQuery.""" @@ -106,6 +118,15 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): inputs.data_interval_end, ) + should_resume, details = await should_resume_from_activity_heartbeat(activity, BigQueryHeartbeatDetails, logger) + + if should_resume is True and details is not None: + data_interval_start = details.last_inserted_at.isoformat() + last_inserted_at = details.last_inserted_at + else: + data_interval_start = inputs.data_interval_start + last_inserted_at = None + async with get_client() as client: if not await client.is_alive(): raise ConnectionError("Cannot establish connection to ClickHouse") @@ -113,7 +134,7 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): count = await get_rows_count( client=client, team_id=inputs.team_id, - interval_start=inputs.data_interval_start, + interval_start=data_interval_start, interval_end=inputs.data_interval_end, exclude_events=inputs.exclude_events, include_events=inputs.include_events, @@ -132,7 +153,7 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): results_iterator = get_results_iterator( client=client, team_id=inputs.team_id, - interval_start=inputs.data_interval_start, + interval_start=data_interval_start, interval_end=inputs.data_interval_end, exclude_events=inputs.exclude_events, include_events=inputs.include_events, @@ -153,8 +174,24 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): ] json_columns = ("properties", "elements", "set", "set_once") + result = None + + async def worker_shutdown_handler(): + """Handle the Worker shutting down by heart-beating our latest status.""" + await activity.wait_for_worker_shutdown() + logger.bind(last_inserted_at=last_inserted_at).debug("Worker shutting down!") + + if last_inserted_at is None: + # Don't heartbeat if worker shuts down before we could even send anything + # Just start from the beginning again. + return + + activity.heartbeat(last_inserted_at) + + asyncio.create_task(worker_shutdown_handler()) + with bigquery_client(inputs) as bq_client: - bigquery_table = create_table_in_bigquery( + bigquery_table = await create_table_in_bigquery( inputs.project_id, inputs.dataset_id, inputs.table_id, @@ -166,13 +203,13 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): rows_exported = get_rows_exported_metric() bytes_exported = get_bytes_exported_metric() - def flush_to_bigquery(): + async def flush_to_bigquery(): logger.debug( "Loading %s records of size %s bytes", jsonl_file.records_since_last_reset, jsonl_file.bytes_since_last_reset, ) - load_jsonl_file_to_bigquery_table(jsonl_file, bigquery_table, table_schema, bq_client) + await load_jsonl_file_to_bigquery_table(jsonl_file, bigquery_table, table_schema, bq_client) rows_exported.add(jsonl_file.records_since_last_reset) bytes_exported.add(jsonl_file.bytes_since_last_reset) @@ -188,11 +225,20 @@ def flush_to_bigquery(): jsonl_file.write_records_to_jsonl([row]) if jsonl_file.tell() > settings.BATCH_EXPORT_BIGQUERY_UPLOAD_CHUNK_SIZE_BYTES: - flush_to_bigquery() + await flush_to_bigquery() + + last_inserted_at = result["inserted_at"] + activity.heartbeat(last_inserted_at) + jsonl_file.reset() - if jsonl_file.tell() > 0: - flush_to_bigquery() + if jsonl_file.tell() > 0 and result is not None: + await flush_to_bigquery() + + last_inserted_at = result["inserted_at"] + activity.heartbeat(last_inserted_at) + + jsonl_file.reset() @workflow.defn(name="bigquery-export") @@ -263,6 +309,4 @@ async def run(self, inputs: BigQueryBatchExportInputs): "NotFound", ], update_inputs=update_inputs, - # Disable heartbeat timeout until we add heartbeat support. - heartbeat_timeout_seconds=None, ) diff --git a/posthog/test/base.py b/posthog/test/base.py index 3cd830a68a49d..21a2fc17c68b7 100644 --- a/posthog/test/base.py +++ b/posthog/test/base.py @@ -460,6 +460,13 @@ def assertQueryMatchesSnapshot(self, query, params=None, replace_all_numbers=Fal if replace_all_numbers: query = re.sub(r"(\"?) = \d+", r"\1 = 2", query) query = re.sub(r"(\"?) IN \(\d+(, \d+)*\)", r"\1 IN (1, 2, 3, 4, 5 /* ... */)", query) + # replace "uuid" IN ('00000000-0000-4000-8000-000000000001'::uuid) effectively: + query = re.sub( + r"\"uuid\" IN \('[0-9a-f-]{36}'(::uuid)?(, '[0-9a-f-]{36}'(::uuid)?)*\)", + r""""uuid" IN ('00000000-0000-0000-0000-000000000000'::uuid, '00000000-0000-0000-0000-000000000001'::uuid /* ... */)\n""", + query, + ) + else: query = re.sub(r"(team|cohort)_id(\"?) = \d+", r"\1_id\2 = 2", query) query = re.sub(r"\d+ as (team|cohort)_id(\"?)", r"2 as \1_id\2", query) @@ -468,6 +475,9 @@ def assertQueryMatchesSnapshot(self, query, params=None, replace_all_numbers=Fal query = re.sub(r"flag_\d+_condition", r"flag_X_condition", query) query = re.sub(r"flag_\d+_super_condition", r"flag_X_super_condition", query) + # replace django cursors + query = re.sub(r"_django_curs_[0-9sync_]*\"", r'_django_curs_X"', query) + # hog ql checks team ids differently query = re.sub( r"equals\(([^.]+\.)?team_id?, \d+\)", diff --git a/posthog/test/test_feature_flag.py b/posthog/test/test_feature_flag.py index da323dd5cb32f..d1056f26e5722 100644 --- a/posthog/test/test_feature_flag.py +++ b/posthog/test/test_feature_flag.py @@ -3458,6 +3458,80 @@ def test_cohort_filters_with_override_properties(self): FeatureFlagMatch(True, None, FeatureFlagMatchReason.CONDITION_MATCH, 0), ) + def test_cohort_filters_with_override_id_property(self): + cohort1 = Cohort.objects.create( + team=self.team, + groups=[ + { + "properties": [ + { + "key": "email", + "type": "person", + "value": "@posthog.com", + "negation": False, + "operator": "icontains", + } + ] + } + ], + name="cohort1", + ) + + feature_flag1: FeatureFlag = self.create_feature_flag( + key="x1", + filters={"groups": [{"properties": [{"key": "id", "value": cohort1.pk, "type": "cohort"}]}]}, + ) + Person.objects.create( + team=self.team, + distinct_ids=["example_id"], + properties={"email": "tim@posthog.com"}, + ) + + with self.assertNumQueries(5): + self.assertEqual( + FeatureFlagMatcher( + [feature_flag1], + "example_id", + property_value_overrides={}, + ).get_match(feature_flag1), + FeatureFlagMatch(True, None, FeatureFlagMatchReason.CONDITION_MATCH, 0), + ) + + with self.assertNumQueries(4): + # no local computation because cohort lookup is required + # no postgres person query required here to get the person, because email is sufficient + # property id override shouldn't confuse the matcher + self.assertEqual( + FeatureFlagMatcher( + [feature_flag1], + "example_id", + property_value_overrides={"id": "example_id", "email": "bzz"}, + ).get_match(feature_flag1), + FeatureFlagMatch(False, None, FeatureFlagMatchReason.NO_CONDITION_MATCH, 0), + ) + + with self.assertNumQueries(4): + # no postgres query required here to get the person + self.assertEqual( + FeatureFlagMatcher( + [feature_flag1], + "example_id", + property_value_overrides={"id": "second_id", "email": "neil@posthog.com"}, + ).get_match(feature_flag1), + FeatureFlagMatch(True, None, FeatureFlagMatchReason.CONDITION_MATCH, 0), + ) + + with self.assertNumQueries(5): + # postgres query required here to get the person + self.assertEqual( + FeatureFlagMatcher( + [feature_flag1], + "example_id", + property_value_overrides={"id": "second_id"}, + ).get_match(feature_flag1), + FeatureFlagMatch(True, None, FeatureFlagMatchReason.CONDITION_MATCH, 0), + ) + @pytest.mark.skip("This case is not supported yet") def test_complex_cohort_filter_with_override_properties(self): # TODO: Currently we don't support this case for persons who haven't been ingested yet diff --git a/posthog/test/test_middleware.py b/posthog/test/test_middleware.py index b5efb9c731891..88e2ba6813f6e 100644 --- a/posthog/test/test_middleware.py +++ b/posthog/test/test_middleware.py @@ -116,7 +116,7 @@ class TestAutoProjectMiddleware(APIBaseTest): @classmethod def setUpTestData(cls): super().setUpTestData() - cls.base_app_num_queries = 40 + cls.base_app_num_queries = 41 # Create another team that the user does have access to cls.second_team = Team.objects.create(organization=cls.organization, name="Second Life") diff --git a/posthog/test/test_user_permissions.py b/posthog/test/test_user_permissions.py index da9faef7330ad..b0562dbca57af 100644 --- a/posthog/test/test_user_permissions.py +++ b/posthog/test/test_user_permissions.py @@ -321,7 +321,7 @@ def test_dashboard_efficiency(self): assert user_permissions.insight(insight).effective_privilege_level is not None def test_team_lookup_efficiency(self): - user = User.objects.create(email="test2@posthog.com") + user = User.objects.create(email="test2@posthog.com", distinct_id="test2") models = [] for _ in range(10): organization, membership, team = Organization.objects.bootstrap( diff --git a/requirements.txt b/requirements.txt index 1efa70e5318f9..752fd3e600466 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,11 +4,13 @@ # # pip-compile requirements.in # -aioboto3==11.1 +aioboto3==11.1.0 # via -r requirements.in aiobotocore[boto3]==2.5.0 - # via aioboto3 -aiohttp==3.8.5 + # via + # aioboto3 + # aiobotocore +aiohttp==3.8.6 # via # -r requirements.in # aiobotocore @@ -223,6 +225,7 @@ geoip2==4.6.0 # via -r requirements.in google-api-core[grpc]==2.11.1 # via + # google-api-core # google-cloud-bigquery # google-cloud-core google-auth==2.22.0 diff --git a/tsconfig.json b/tsconfig.json index 658bedd03e802..d00e7af6a118b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,7 +34,7 @@ "lib": ["dom", "es2019"] }, "include": ["frontend/**/*", ".storybook/**/*"], - "exclude": ["node_modules/**/*", "staticfiles/**/*", "frontend/dist/**/*", "plugin-server/**/*"], + "exclude": ["frontend/dist/**/*"], "ts-node": { "compilerOptions": { "module": "commonjs"