diff --git a/README.md b/README.md index 45ae4ebc8aae7..6776b643722fd 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ - Analyze data with ready-made visualizations, or do it yourself with SQL - Only capture properties on the people you want to track, save money when you don't - Gather insights by capturing session replays, console logs, and network monitoring -- Improve your product with A/B testing that automatically analyzes performance +- Improve your product with Experiments that automatically analyze performance - Safely roll out features to select users or cohorts with feature flags - Send out fully customizable surveys to specific cohorts of users - Connect to external services and manage data flows with PostHog CDP @@ -81,7 +81,7 @@ PostHog brings all the tools and data you need to build better products. - **Session replays:** [Watch videos](https://posthog.com/docs/features/session-recording) of your users' behavior, with fine-grained filters and privacy controls, as well as network monitoring and captured console logs - **Heatmaps:** See where users click and get a visual representation of their behaviour with the [PostHog Toolbar](https://posthog.com/docs/features/toolbar) - **Feature flags:** Test and manage the rollout of [new features](https://posthog.com/docs/feature-flags/installation) to specific users and groups, or deploy flags as kill-switches -- **A/B and multivariate experimentation:** run simple or complex changes as [experiments](https://posthog.com/manual/experimentation) and get automatic significance calculations +- **Experiments:** run simple or complex changes as [experiments](https://posthog.com/manual/experimentation) and get automatic significance calculations - **Correlation analysis:** Discover what events and properties [correlate](https://posthog.com/manual/correlation) with success and failure - **Surveys:** Collect qualitative feedback from your users using fully customizable [surveys](https://posthog.com/docs/surveys/installation) diff --git a/bin/upgrade-hobby b/bin/upgrade-hobby index 0e75aeb6352b1..2c882978618e9 100644 --- a/bin/upgrade-hobby +++ b/bin/upgrade-hobby @@ -11,6 +11,13 @@ else exit fi +if [ "$REGISTRY_URL" == "" ] +then +export REGISTRY_URL="posthog/posthog" +fi + +export POSTHOG_APP_TAG="${POSTHOG_APP_TAG:-latest}" + echo "Checking for named postgres and clickhouse volumes to avoid data loss when upgrading from < 1.39" if docker volume ls | grep -Pzoq 'clickhouse-data\n(.|\n)*postgres-data\n' then diff --git a/cypress/e2e/experiments.cy.ts b/cypress/e2e/experiments.cy.ts index af8da82929a2f..5f432e1e41dc1 100644 --- a/cypress/e2e/experiments.cy.ts +++ b/cypress/e2e/experiments.cy.ts @@ -17,7 +17,7 @@ describe('Experiments', () => { it('create experiment', () => { cy.visit('/experiments') - cy.get('[data-attr=top-bar-name]').should('contain', 'A/B testing') + cy.get('[data-attr=top-bar-name]').should('contain', 'Experiments') // Name, flag key, description cy.get('[data-attr=create-experiment]').first().click() diff --git a/docker-compose.hobby.yml b/docker-compose.hobby.yml index d268e570a3ca9..0b00d510ad754 100644 --- a/docker-compose.hobby.yml +++ b/docker-compose.hobby.yml @@ -24,6 +24,14 @@ services: service: redis volumes: - redis-data:/data + + redis7: + extends: + file: docker-compose.base.yml + service: redis7 + volumes: + - redis7-data:/data + clickhouse: # # Note: please keep the default version in sync across @@ -112,9 +120,11 @@ services: OBJECT_STORAGE_SECRET_ACCESS_KEY: 'object_storage_root_password' OBJECT_STORAGE_ENDPOINT: http://objectstorage:19000 OBJECT_STORAGE_ENABLED: true + CDP_REDIS_HOST: redis7 depends_on: - db - redis + - redis7 - clickhouse - kafka - objectstorage @@ -215,4 +225,5 @@ volumes: caddy-data: caddy-config: redis-data: + redis7-data: kafka-data: diff --git a/ee/api/feature_flag_role_access.py b/ee/api/feature_flag_role_access.py index f0bd176484ee4..6d03c7a4f361c 100644 --- a/ee/api/feature_flag_role_access.py +++ b/ee/api/feature_flag_role_access.py @@ -77,7 +77,7 @@ class FeatureFlagRoleAccessViewSet( permission_classes = [FeatureFlagRoleAccessPermissions] serializer_class = FeatureFlagRoleAccessSerializer queryset = FeatureFlagRoleAccess.objects.select_related("feature_flag") - filter_rewrite_rules = {"team_id": "feature_flag__team_id"} + filter_rewrite_rules = {"project_id": "feature_flag__team__project_id"} def safely_get_queryset(self, queryset): filters = self.request.GET.dict() diff --git a/ee/billing/test/test_billing_manager.py b/ee/billing/test/test_billing_manager.py index 1c5ed9d2ba859..4672baa98d83e 100644 --- a/ee/billing/test/test_billing_manager.py +++ b/ee/billing/test/test_billing_manager.py @@ -18,7 +18,7 @@ def create_default_products_response(**kwargs) -> dict[str, list[Product]]: Product( name="Product analytics", headline="Product analytics with autocapture", - description="A comprehensive product analytics platform built to natively work with session replay, feature flags, A/B testing, and surveys.", + description="A comprehensive product analytics platform built to natively work with session replay, feature flags, experiments, and surveys.", usage_key="events", image_url="https://posthog.com/images/products/product-analytics/product-analytics.png", docs_url="https://posthog.com/docs/product-analytics", diff --git a/ee/clickhouse/queries/experiments/funnel_experiment_result.py b/ee/clickhouse/queries/experiments/funnel_experiment_result.py index ea95e177a5c80..4df48b0c97b0a 100644 --- a/ee/clickhouse/queries/experiments/funnel_experiment_result.py +++ b/ee/clickhouse/queries/experiments/funnel_experiment_result.py @@ -176,13 +176,13 @@ def calculate_results( if len(test_variants) >= 10: raise ValidationError( - "Can't calculate A/B test results for more than 10 variants", + "Can't calculate experiment results for more than 10 variants", code="too_much_data", ) if len(test_variants) < 1: raise ValidationError( - "Can't calculate A/B test results for less than 2 variants", + "Can't calculate experiment results for less than 2 variants", code="no_data", ) @@ -311,7 +311,7 @@ def calculate_probability_of_winning_for_each(variants: list[Variant]) -> list[P """ if len(variants) > 10: raise ValidationError( - "Can't calculate A/B test results for more than 10 variants", + "Can't calculate experiment results for more than 10 variants", code="too_much_data", ) diff --git a/ee/clickhouse/queries/experiments/trend_experiment_result.py b/ee/clickhouse/queries/experiments/trend_experiment_result.py index 781d5690f006e..489520fd02596 100644 --- a/ee/clickhouse/queries/experiments/trend_experiment_result.py +++ b/ee/clickhouse/queries/experiments/trend_experiment_result.py @@ -338,13 +338,13 @@ def calculate_results(control_variant: Variant, test_variants: list[Variant]) -> if len(test_variants) >= 10: raise ValidationError( - "Can't calculate A/B test results for more than 10 variants", + "Can't calculate experiment results for more than 10 variants", code="too_much_data", ) if len(test_variants) < 1: raise ValidationError( - "Can't calculate A/B test results for less than 2 variants", + "Can't calculate experiment results for less than 2 variants", code="no_data", ) @@ -413,7 +413,7 @@ def calculate_probability_of_winning_for_each(variants: list[Variant]) -> list[P if len(variants) > 10: raise ValidationError( - "Can't calculate A/B test results for more than 10 variants", + "Can't calculate experiment results for more than 10 variants", code="too_much_data", ) @@ -447,7 +447,7 @@ def intermediate_poisson_term(count: int, iterator: int, relative_exposure: floa def poisson_p_value(control_count, control_exposure, test_count, test_exposure): """ - Calculates the p-value of the A/B test. + Calculates the p-value of the experiment. Calculations from: https://www.evanmiller.org/statistical-formulas-for-programmers.html#count_test """ relative_exposure = test_exposure / (control_exposure + test_exposure) diff --git a/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png b/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png index a1b68be569fea..0672903871c50 100644 Binary files a/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png and b/frontend/__snapshots__/filters-property-filter-button--filter-types--dark.png differ diff --git a/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png b/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png index a74328c260a96..4873b57513359 100644 Binary files a/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png and b/frontend/__snapshots__/filters-property-filter-button--filter-types--light.png differ diff --git a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png index cc74b8c8cc166..4a7f487fb6b79 100644 Binary files a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png and b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-errortracking--group-page--dark.png b/frontend/__snapshots__/scenes-app-errortracking--group-page--dark.png index 81e9d428f91a1..13c26071b3bcc 100644 Binary files a/frontend/__snapshots__/scenes-app-errortracking--group-page--dark.png and b/frontend/__snapshots__/scenes-app-errortracking--group-page--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-errortracking--group-page--light.png b/frontend/__snapshots__/scenes-app-errortracking--group-page--light.png index f6d202d44c7b1..5a0cbd2405b75 100644 Binary files a/frontend/__snapshots__/scenes-app-errortracking--group-page--light.png and b/frontend/__snapshots__/scenes-app-errortracking--group-page--light.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--experiments-list--dark.png b/frontend/__snapshots__/scenes-app-experiments--experiments-list--dark.png index 9944485ce6c2f..b447a10f2dec2 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--experiments-list--dark.png and b/frontend/__snapshots__/scenes-app-experiments--experiments-list--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-experiments--experiments-list--light.png b/frontend/__snapshots__/scenes-app-experiments--experiments-list--light.png index def9e9eeb9007..e631f63618026 100644 Binary files a/frontend/__snapshots__/scenes-app-experiments--experiments-list--light.png and b/frontend/__snapshots__/scenes-app-experiments--experiments-list--light.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png b/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png index b213cb8e6c970..1308913c32079 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png and b/frontend/__snapshots__/scenes-app-insights--user-paths-edit--dark.png differ diff --git a/frontend/src/layout/navigation-3000/navigationLogic.tsx b/frontend/src/layout/navigation-3000/navigationLogic.tsx index 9764bd6fb71a2..b1eddf98ad684 100644 --- a/frontend/src/layout/navigation-3000/navigationLogic.tsx +++ b/frontend/src/layout/navigation-3000/navigationLogic.tsx @@ -475,7 +475,7 @@ export const navigation3000Logic = kea([ }, { identifier: Scene.Experiments, - label: 'A/B testing', + label: 'Experiments', icon: , logic: isUsingSidebar ? experimentsSidebarLogic : undefined, to: isUsingSidebar ? undefined : urls.experiments(), diff --git a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts index 210204936359e..5eeaff0109f63 100644 --- a/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts +++ b/frontend/src/layout/navigation-3000/sidebars/dataManagement.ts @@ -117,7 +117,7 @@ export const dataManagementSidebarLogic = kea([ menuItems: [ { label: 'View recordings', - to: urls.replay(ReplayTabs.Recent, { + to: urls.replay(ReplayTabs.Home, { filter_group: { type: FilterLogicalOperator.And, values: [ diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx index 74e4653f2d776..f1f6a5a1e8a7b 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/SidePanelSupport.tsx @@ -60,7 +60,7 @@ const PRODUCTS = [ icon: , }, { - name: 'A/B testing', + name: 'Experiments', slug: 'experiments', icon: , }, diff --git a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss index b21e228a3ef63..9a8afaa08b7af 100644 --- a/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss +++ b/frontend/src/lib/components/Cards/InsightCard/InsightCard.scss @@ -16,14 +16,6 @@ outline: 1px solid var(--primary-3000); } - &.react-draggable::after { - // During dashboard layout editing, we need an overlay to prevent accidental interaction with card content - position: absolute; - inset: 0; - z-index: var(--z-content-overlay); - content: ''; - } - .ErrorBoundary { width: 100%; height: 100%; diff --git a/frontend/src/lib/components/Cards/handles.tsx b/frontend/src/lib/components/Cards/handles.tsx index 5e25bd8f244a7..68d82e2b59853 100644 --- a/frontend/src/lib/components/Cards/handles.tsx +++ b/frontend/src/lib/components/Cards/handles.tsx @@ -5,8 +5,8 @@ export function ResizeHandle1D({ orientation }: { orientation: 'horizontal' | 'v return (
- - + + @@ -16,7 +16,7 @@ export function ResizeHandle1D({ orientation }: { orientation: 'horizontal' | 'v - +
) @@ -27,8 +27,8 @@ export function ResizeHandle2D(): JSX.Element { return (
- - + + @@ -36,7 +36,7 @@ export function ResizeHandle2D(): JSX.Element { - +
) diff --git a/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx b/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx index 9f1f2d5cfda9d..e462fed6e1c8f 100644 --- a/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx +++ b/frontend/src/lib/components/CommandPalette/commandPaletteLogic.tsx @@ -589,7 +589,7 @@ export const commandPaletteLogic = kea([ }, { icon: IconTestTube, - display: 'Go to A/B testing', + display: 'Go to Experiments', executor: () => { push(urls.experiments()) }, diff --git a/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx new file mode 100644 index 0000000000000..3e34575b31c96 --- /dev/null +++ b/frontend/src/lib/components/IframedToolbarBrowser/IframedToolbarBrowser.tsx @@ -0,0 +1,75 @@ +import { LemonBanner, Spinner } from '@posthog/lemon-ui' +import { useActions, useValues } from 'kea' +import { useEffect } from 'react' +import useResizeObserver from 'use-resize-observer' + +import { ToolbarUserIntent } from '~/types' + +import { appEditorUrl } from '../AuthorizedUrlList/authorizedUrlListLogic' +import { iframedToolbarBrowserLogic } from './iframedToolbarBrowserLogic' + +function IframeErrorOverlay(): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + const { iframeBanner } = useValues(logic) + return iframeBanner ? ( +
+ + {iframeBanner.message}. Your site might not allow being embedded in an iframe. You can click "Open in + toolbar" above to visit your site and view the heatmap there. + +
+ ) : null +} + +function LoadingOverlay(): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + const { loading } = useValues(logic) + return loading ? ( +
+ +
+ ) : null +} + +export function IframedToolbarBrowser({ + iframeRef, + userIntent, +}: { + iframeRef?: React.MutableRefObject + userIntent: ToolbarUserIntent +}): JSX.Element | null { + const logic = iframedToolbarBrowserLogic() + + const { browserUrl } = useValues(logic) + const { onIframeLoad, setIframeWidth } = useActions(logic) + + const { width: iframeWidth } = useResizeObserver({ ref: iframeRef }) + useEffect(() => { + setIframeWidth(iframeWidth ?? null) + }, [iframeWidth]) + + return browserUrl ? ( +
+ + +