diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8849e6aefe280..7344b83aef40b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -218,7 +218,7 @@ jobs: - name: Run clippy if: needs.changes.outputs.rust == 'true' - run: cargo clippy -- -D warnings + run: cargo clippy --all-targets --all-features -- -D warnings - name: Run cargo check if: needs.changes.outputs.rust == 'true' diff --git a/cypress/e2e/notebooks-insights.ts b/cypress/e2e/notebooks-insights.ts index 0b007744576c6..9c564692810aa 100644 --- a/cypress/e2e/notebooks-insights.ts +++ b/cypress/e2e/notebooks-insights.ts @@ -5,7 +5,18 @@ describe('Notebooks', () => { cy.clickNavMenu('notebooks') cy.location('pathname').should('include', '/notebooks') }) - ;['SQL', 'TRENDS', 'FUNNELS', 'RETENTION', 'PATHS', 'STICKINESS', 'LIFECYCLE'].forEach((insightType) => { + + it(`Can add a HogQL insight`, () => { + savedInsights.createNewInsightOfType('SQL') + insight.editName('SQL Insight') + insight.save() + cy.get('[data-attr="notebooks-add-button"]').click() + cy.get('[data-attr="notebooks-select-button-create"]').click() + cy.get('.ErrorBoundary').should('not.exist') + // Detect if table settings are present. They shouldn't appear in the block, but rather on side. + cy.get('[data-attr="notebook-node-query"]').get('[data-attr="export-button"]').should('not.exist') + }) + ;['TRENDS', 'FUNNELS', 'RETENTION', 'PATHS', 'STICKINESS', 'LIFECYCLE'].forEach((insightType) => { it(`Can add a ${insightType} insight`, () => { savedInsights.createNewInsightOfType(insightType) insight.editName(`${insightType} Insight`) diff --git a/ee/clickhouse/views/experiment_holdouts.py b/ee/clickhouse/views/experiment_holdouts.py new file mode 100644 index 0000000000000..c7d8eff83ce5a --- /dev/null +++ b/ee/clickhouse/views/experiment_holdouts.py @@ -0,0 +1,110 @@ +from typing import Any +from rest_framework import serializers, viewsets +from rest_framework.exceptions import ValidationError +from rest_framework.request import Request +from rest_framework.response import Response +from django.db import transaction + + +from posthog.api.feature_flag import FeatureFlagSerializer +from posthog.api.routing import TeamAndOrgViewSetMixin +from posthog.api.shared import UserBasicSerializer +from posthog.models.experiment import ExperimentHoldout + + +class ExperimentHoldoutSerializer(serializers.ModelSerializer): + created_by = UserBasicSerializer(read_only=True) + + class Meta: + model = ExperimentHoldout + fields = [ + "id", + "name", + "description", + "filters", + "created_by", + "created_at", + "updated_at", + ] + read_only_fields = [ + "id", + "created_by", + "created_at", + "updated_at", + ] + + def _get_filters_with_holdout_id(self, id: int, filters: list) -> list: + variant_key = f"holdout-{id}" + updated_filters = [] + for filter in filters: + updated_filters.append( + { + **filter, + "variant": variant_key, + } + ) + return updated_filters + + def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> ExperimentHoldout: + request = self.context["request"] + validated_data["created_by"] = request.user + validated_data["team_id"] = self.context["team_id"] + + if not validated_data.get("filters"): + raise ValidationError("Filters are required to create an holdout group") + + instance = super().create(validated_data) + instance.filters = self._get_filters_with_holdout_id(instance.id, instance.filters) + instance.save() + return instance + + def update(self, instance: ExperimentHoldout, validated_data): + filters = validated_data.get("filters") + if filters and instance.filters != filters: + # update flags on all experiments in this holdout group + new_filters = self._get_filters_with_holdout_id(instance.id, filters) + validated_data["filters"] = new_filters + with transaction.atomic(): + for experiment in instance.experiment_set.all(): + flag = experiment.feature_flag + existing_flag_serializer = FeatureFlagSerializer( + flag, + data={ + "filters": {**flag.filters, "holdout_groups": validated_data["filters"]}, + }, + partial=True, + context=self.context, + ) + existing_flag_serializer.is_valid(raise_exception=True) + existing_flag_serializer.save() + + return super().update(instance, validated_data) + + +class ExperimentHoldoutViewSet(TeamAndOrgViewSetMixin, viewsets.ModelViewSet): + scope_object = "experiment" + queryset = ExperimentHoldout.objects.prefetch_related("created_by").all() + serializer_class = ExperimentHoldoutSerializer + ordering = "-created_at" + + def destroy(self, request: Request, *args: Any, **kwargs: Any) -> Response: + instance = self.get_object() + + with transaction.atomic(): + for experiment in instance.experiment_set.all(): + flag = experiment.feature_flag + existing_flag_serializer = FeatureFlagSerializer( + flag, + data={ + "filters": { + **flag.filters, + "holdout_groups": None, + } + }, + partial=True, + context={"request": request, "team": self.team, "team_id": self.team_id}, + ) + existing_flag_serializer.is_valid(raise_exception=True) + existing_flag_serializer.save() + + return super().destroy(request, *args, **kwargs) diff --git a/ee/clickhouse/views/experiments.py b/ee/clickhouse/views/experiments.py index 7aed519d29ee6..5a389073ef6b7 100644 --- a/ee/clickhouse/views/experiments.py +++ b/ee/clickhouse/views/experiments.py @@ -167,6 +167,7 @@ class Meta: "end_date", "feature_flag_key", "feature_flag", + "holdout", "exposure_cohort", "parameters", "secondary_metrics", @@ -221,6 +222,10 @@ def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment: if properties: raise ValidationError("Experiments do not support global filter properties") + holdout_groups = None + if validated_data.get("holdout"): + holdout_groups = validated_data["holdout"].filters + default_variants = [ {"key": "control", "name": "Control Group", "rollout_percentage": 50}, {"key": "test", "name": "Test Variant", "rollout_percentage": 50}, @@ -230,6 +235,7 @@ def create(self, validated_data: dict, *args: Any, **kwargs: Any) -> Experiment: "groups": [{"properties": properties, "rollout_percentage": 100}], "multivariate": {"variants": variants or default_variants}, "aggregation_group_type_index": aggregation_group_type_index, + "holdout_groups": holdout_groups, } feature_flag_serializer = FeatureFlagSerializer( @@ -263,6 +269,7 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg "parameters", "archived", "secondary_metrics", + "holdout", } given_keys = set(validated_data.keys()) extra_keys = given_keys - expected_keys @@ -273,7 +280,7 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg if extra_keys: raise ValidationError(f"Can't update keys: {', '.join(sorted(extra_keys))} on Experiment") - # if an experiment has launched, we cannot edit its variants anymore. + # if an experiment has launched, we cannot edit its variants or holdout anymore. if not instance.is_draft: if "feature_flag_variants" in validated_data.get("parameters", {}): if len(validated_data["parameters"]["feature_flag_variants"]) != len(feature_flag.variants): @@ -285,13 +292,19 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg != 1 ): raise ValidationError("Can't update feature_flag_variants on Experiment") + if "holdout" in validated_data and validated_data["holdout"] != instance.holdout: + raise ValidationError("Can't update holdout on running Experiment") properties = validated_data.get("filters", {}).get("properties") if properties: raise ValidationError("Experiments do not support global filter properties") if instance.is_draft: - # if feature flag variants have changed, update the feature flag. + # if feature flag variants or holdout have changed, update the feature flag. + holdout_groups = instance.holdout.filters if instance.holdout else None + if "holdout" in validated_data: + holdout_groups = validated_data["holdout"].filters if validated_data["holdout"] else None + if validated_data.get("parameters"): variants = validated_data["parameters"].get("feature_flag_variants", []) aggregation_group_type_index = validated_data["parameters"].get("aggregation_group_type_index") @@ -312,6 +325,7 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg "groups": [{"properties": properties, "rollout_percentage": 100}], "multivariate": {"variants": variants or default_variants}, "aggregation_group_type_index": aggregation_group_type_index, + "holdout_groups": holdout_groups, } existing_flag_serializer = FeatureFlagSerializer( @@ -322,6 +336,17 @@ def update(self, instance: Experiment, validated_data: dict, *args: Any, **kwarg ) existing_flag_serializer.is_valid(raise_exception=True) existing_flag_serializer.save() + else: + # no parameters provided, just update the holdout if necessary + if "holdout" in validated_data: + existing_flag_serializer = FeatureFlagSerializer( + feature_flag, + data={"filters": {**feature_flag.filters, "holdout_groups": holdout_groups}}, + partial=True, + context=self.context, + ) + existing_flag_serializer.is_valid(raise_exception=True) + existing_flag_serializer.save() if instance.is_draft and has_start_date: feature_flag.active = True diff --git a/ee/clickhouse/views/test/test_clickhouse_experiments.py b/ee/clickhouse/views/test/test_clickhouse_experiments.py index fdd0c05656c7c..2db87bf8965b2 100644 --- a/ee/clickhouse/views/test/test_clickhouse_experiments.py +++ b/ee/clickhouse/views/test/test_clickhouse_experiments.py @@ -123,6 +123,192 @@ def test_creating_updating_basic_experiment(self): self.assertEqual(experiment.description, "Bazinga") self.assertEqual(experiment.end_date.strftime("%Y-%m-%dT%H:%M"), end_date) + def test_transferring_holdout_to_another_group(self): + response = self.client.post( + f"/api/projects/{self.team.id}/experiment_holdouts/", + data={ + "name": "Test Experiment holdout", + "filters": [ + { + "properties": [], + "rollout_percentage": 20, + "variant": "holdout", + } + ], + }, + format="json", + ) + + holdout_id = response.json()["id"] + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.json()["name"], "Test Experiment holdout") + self.assertEqual( + response.json()["filters"], + [{"properties": [], "rollout_percentage": 20, "variant": f"holdout-{holdout_id}"}], + ) + + # Generate draft experiment to be part of holdout + ff_key = "a-b-tests" + response = self.client.post( + f"/api/projects/{self.team.id}/experiments/", + { + "name": "Test Experiment", + "description": "", + "start_date": None, + "end_date": None, + "feature_flag_key": ff_key, + "parameters": None, + "filters": { + "events": [ + {"order": 0, "id": "$pageview"}, + {"order": 1, "id": "$pageleave"}, + ], + "properties": [], + }, + "holdout": holdout_id, + }, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.json()["name"], "Test Experiment") + self.assertEqual(response.json()["feature_flag_key"], ff_key) + + created_ff = FeatureFlag.objects.get(key=ff_key) + + self.assertEqual(created_ff.key, ff_key) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 20, "variant": f"holdout-{holdout_id}"}], + ) + + exp_id = response.json()["id"] + + # new holdout, and update experiment + response = self.client.post( + f"/api/projects/{self.team.id}/experiment_holdouts/", + data={ + "name": "Test Experiment holdout 2", + "filters": [ + { + "properties": [], + "rollout_percentage": 5, + "variant": "holdout", + } + ], + }, + format="json", + ) + holdout_2_id = response.json()["id"] + + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"holdout": holdout_2_id}, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + experiment = Experiment.objects.get(pk=exp_id) + self.assertEqual(experiment.holdout_id, holdout_2_id) + + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 5, "variant": f"holdout-{holdout_2_id}"}], + ) + + # update parameters + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + { + "parameters": { + "feature_flag_variants": [ + { + "key": "control", + "name": "Control Group", + "rollout_percentage": 33, + }, + { + "key": "test_1", + "name": "Test Variant", + "rollout_percentage": 33, + }, + { + "key": "test_2", + "name": "Test Variant", + "rollout_percentage": 34, + }, + ] + }, + }, + ) + + experiment = Experiment.objects.get(pk=exp_id) + self.assertEqual(experiment.holdout_id, holdout_2_id) + + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 5, "variant": f"holdout-{holdout_2_id}"}], + ) + self.assertEqual( + created_ff.filters["multivariate"]["variants"], + [ + {"key": "control", "name": "Control Group", "rollout_percentage": 33}, + {"key": "test_1", "name": "Test Variant", "rollout_percentage": 33}, + {"key": "test_2", "name": "Test Variant", "rollout_percentage": 34}, + ], + ) + + # remove holdouts + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"holdout": None}, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + experiment = Experiment.objects.get(pk=exp_id) + self.assertEqual(experiment.holdout_id, None) + + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual(created_ff.filters["holdout_groups"], None) + + # try adding invalid holdout + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"holdout": 123456}, + ) + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json()["detail"], 'Invalid pk "123456" - object does not exist.') + + # add back holdout + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"holdout": holdout_2_id}, + ) + + # launch experiment and try updating holdouts again + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"start_date": "2021-12-01T10:23"}, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + response = self.client.patch( + f"/api/projects/{self.team.id}/experiments/{exp_id}", + {"holdout": holdout_id}, + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json()["detail"], "Can't update holdout on running Experiment") + + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 5, "variant": f"holdout-{holdout_2_id}"}], + ) + def test_adding_behavioral_cohort_filter_to_experiment_fails(self): cohort = Cohort.objects.create( team=self.team, @@ -1119,6 +1305,7 @@ def test_create_experiment_updates_feature_flag_cache(self): ] }, "aggregation_group_type_index": None, + "holdout_groups": None, }, ) @@ -1170,6 +1357,7 @@ def test_create_experiment_updates_feature_flag_cache(self): ] }, "aggregation_group_type_index": None, + "holdout_groups": None, }, ) @@ -1237,6 +1425,7 @@ def test_create_experiment_updates_feature_flag_cache(self): ] }, "aggregation_group_type_index": None, + "holdout_groups": None, }, ) diff --git a/ee/clickhouse/views/test/test_experiment_holdouts.py b/ee/clickhouse/views/test/test_experiment_holdouts.py new file mode 100644 index 0000000000000..37e9dc7b25e5f --- /dev/null +++ b/ee/clickhouse/views/test/test_experiment_holdouts.py @@ -0,0 +1,145 @@ +from rest_framework import status + +from ee.api.test.base import APILicensedTest +from posthog.models.experiment import Experiment +from posthog.models.feature_flag import FeatureFlag + + +class TestExperimentHoldoutCRUD(APILicensedTest): + def test_can_list_experiment_holdouts(self): + response = self.client.get(f"/api/projects/{self.team.id}/experiment_holdouts/") + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_create_update_experiment_holdouts(self) -> None: + response = self.client.post( + f"/api/projects/{self.team.id}/experiment_holdouts/", + data={ + "name": "Test Experiment holdout", + "filters": [ + { + "properties": [], + "rollout_percentage": 20, + "variant": "holdout", + } + ], + }, + format="json", + ) + + holdout_id = response.json()["id"] + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.json()["name"], "Test Experiment holdout") + self.assertEqual( + response.json()["filters"], + [{"properties": [], "rollout_percentage": 20, "variant": f"holdout-{holdout_id}"}], + ) + + # Generate experiment to be part of holdout + ff_key = "a-b-tests" + response = self.client.post( + f"/api/projects/{self.team.id}/experiments/", + { + "name": "Test Experiment", + "description": "", + "start_date": "2021-12-01T10:23", + "end_date": None, + "feature_flag_key": ff_key, + "parameters": None, + "filters": { + "events": [ + {"order": 0, "id": "$pageview"}, + {"order": 1, "id": "$pageleave"}, + ], + "properties": [], + }, + "holdout": holdout_id, + }, + ) + + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + self.assertEqual(response.json()["name"], "Test Experiment") + self.assertEqual(response.json()["feature_flag_key"], ff_key) + + created_ff = FeatureFlag.objects.get(key=ff_key) + + self.assertEqual(created_ff.key, ff_key) + self.assertEqual(created_ff.filters["multivariate"]["variants"][0]["key"], "control") + self.assertEqual(created_ff.filters["multivariate"]["variants"][1]["key"], "test") + self.assertEqual(created_ff.filters["groups"][0]["properties"], []) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 20, "variant": f"holdout-{holdout_id}"}], + ) + + exp_id = response.json()["id"] + # Now try updating holdout + response = self.client.patch( + f"/api/projects/{self.team.id}/experiment_holdouts/{holdout_id}", + { + "name": "Test Experiment holdout 2", + "filters": [ + { + "properties": [], + "rollout_percentage": 30, + "variant": "holdout", + } + ], + }, + ) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.json()["name"], "Test Experiment holdout 2") + self.assertEqual( + response.json()["filters"], + [{"properties": [], "rollout_percentage": 30, "variant": f"holdout-{holdout_id}"}], + ) + + # make sure flag for experiment in question was updated as well + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual( + created_ff.filters["holdout_groups"], + [{"properties": [], "rollout_percentage": 30, "variant": f"holdout-{holdout_id}"}], + ) + + # now delete holdout + response = self.client.delete(f"/api/projects/{self.team.id}/experiment_holdouts/{holdout_id}") + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + # make sure flag for experiment in question was updated as well + created_ff = FeatureFlag.objects.get(key=ff_key) + self.assertEqual(created_ff.filters["holdout_groups"], None) + + # and same for experiment + exp = Experiment.objects.get(pk=exp_id) + self.assertEqual(exp.holdout, None) + + def test_invalid_create(self): + response = self.client.post( + f"/api/projects/{self.team.id}/experiment_holdouts/", + data={ + "name": None, # invalid + "filters": [ + { + "properties": [], + "rollout_percentage": 20, + "variant": "holdout", + } + ], + }, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json()["detail"], "This field may not be null.") + + response = self.client.post( + f"/api/projects/{self.team.id}/experiment_holdouts/", + data={ + "name": "xyz", + "filters": [], + }, + format="json", + ) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + self.assertEqual(response.json()["detail"], "Filters are required to create an holdout group") diff --git a/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_filters.ambr b/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_filters.ambr index 2a88759e116ee..c21b2882da6ed 100644 --- a/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_filters.ambr +++ b/ee/session_recordings/queries/test/__snapshots__/test_session_recording_list_from_filters.ambr @@ -16,12 +16,13 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_4)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events - WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_12)s), 0)) + WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_9)s), now64(6, %(hogql_val_10)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_13)s), 0)) GROUP BY events.`$session_id` HAVING 1))) GROUP BY s.session_id @@ -47,9 +48,10 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_4)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events LEFT OUTER JOIN @@ -59,12 +61,12 @@ GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) LEFT JOIN - (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_7)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_8)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id FROM person WHERE equals(person.team_id, 2) GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_8)s), person.version), plus(now64(6, %(hogql_val_9)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), now64(6, %(hogql_val_12)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_13)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_9)s), person.version), plus(now64(6, %(hogql_val_10)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_15)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_16)s), 0)) GROUP BY events.`$session_id` HAVING 1))) GROUP BY s.session_id @@ -90,9 +92,10 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_4)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events LEFT OUTER JOIN @@ -102,12 +105,12 @@ GROUP BY person_distinct_id_overrides.distinct_id HAVING ifNull(equals(argMax(person_distinct_id_overrides.is_deleted, person_distinct_id_overrides.version), 0), 0) SETTINGS optimize_aggregation_in_order=1) AS events__override ON equals(events.distinct_id, events__override.distinct_id) LEFT JOIN - (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_7)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id + (SELECT argMax(replaceRegexpAll(nullIf(nullIf(JSONExtractRaw(person.properties, %(hogql_val_8)s), ''), 'null'), '^"|"$', ''), person.version) AS properties___rgInternal, person.id AS id FROM person WHERE equals(person.team_id, 2) GROUP BY person.id - HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_8)s), person.version), plus(now64(6, %(hogql_val_9)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) - WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), now64(6, %(hogql_val_12)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_13)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_15)s), 0)) + HAVING and(ifNull(equals(argMax(person.is_deleted, person.version), 0), 0), ifNull(less(argMax(toTimeZone(person.created_at, %(hogql_val_9)s), person.version), plus(now64(6, %(hogql_val_10)s), toIntervalDay(1))), 0)) SETTINGS optimize_aggregation_in_order=1) AS events__person ON equals(if(not(empty(events__override.distinct_id)), events__override.person_id, events.person_id), events__person.id) + WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), now64(6, %(hogql_val_13)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_14)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_15)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(events__person.properties___rgInternal, %(hogql_val_16)s), 0)) GROUP BY events.`$session_id` HAVING 1))) GROUP BY s.session_id @@ -133,12 +136,13 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_4)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events - WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_12)s), 0)) + WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_9)s), now64(6, %(hogql_val_10)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_13)s), 0)) GROUP BY events.`$session_id` HAVING 1))) GROUP BY s.session_id @@ -164,12 +168,13 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, %(hogql_val_3)s)), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff(%(hogql_val_4)s, start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s - WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_4)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, + WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_5)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_6)s), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, %(hogql_val_7)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id FROM events - WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_7)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), now64(6, %(hogql_val_9)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_10)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_12)s), 0)) + WHERE and(equals(events.team_id, 2), notEmpty(events.`$session_id`), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_8)s), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_9)s), now64(6, %(hogql_val_10)s)), greaterOrEquals(toTimeZone(events.timestamp, %(hogql_val_11)s), toDateTime64('2020-12-24 23:58:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(events.timestamp, %(hogql_val_12)s), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), ifNull(equals(nullIf(nullIf(mat_pp_rgInternal, ''), 'null'), %(hogql_val_13)s), 0)) GROUP BY events.`$session_id` HAVING 1))) GROUP BY s.session_id @@ -195,7 +200,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -228,7 +234,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -266,7 +273,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -299,7 +307,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -337,7 +346,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -370,7 +380,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -408,7 +419,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -441,7 +453,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -479,7 +492,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -512,7 +526,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -565,7 +580,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -598,7 +614,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -651,7 +668,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -684,7 +702,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -737,7 +756,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -770,7 +790,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -823,7 +844,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -856,7 +878,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -894,7 +917,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -927,7 +951,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -965,7 +990,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -998,7 +1024,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -1036,7 +1063,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0)) GROUP BY s.session_id @@ -1069,7 +1097,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-25 00:00:00.000000', 6, 'UTC')), 0), ifNull(lessOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2021-01-01 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT events.`$session_id` AS session_id @@ -1107,7 +1136,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1143,7 +1173,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1179,7 +1210,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1215,7 +1247,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1251,7 +1284,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1293,7 +1327,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1335,7 +1370,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1377,7 +1413,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1419,7 +1456,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1461,7 +1499,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1503,7 +1542,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` @@ -1545,7 +1585,8 @@ sum(s.console_log_count) AS console_log_count, sum(s.console_warn_count) AS console_warn_count, sum(s.console_error_count) AS console_error_count, - ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing + ifNull(greaterOrEquals(max(toTimeZone(s._timestamp, 'UTC')), toDateTime64('2021-01-01 13:41:23.000000', 6, 'UTC')), 0) AS ongoing, + round(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 2) AS activity_score FROM session_replay_events AS s WHERE and(equals(s.team_id, 2), ifNull(greaterOrEquals(toTimeZone(s.min_first_timestamp, 'UTC'), toDateTime64('2020-12-11 13:46:23.000000', 6, 'UTC')), 0), in(s.session_id, (SELECT DISTINCT events.`$session_id` AS `$session_id` diff --git a/frontend/__snapshots__/components-playlist--default--dark.png b/frontend/__snapshots__/components-playlist--default--dark.png index 78a17bfdf2b8b..468e971365877 100644 Binary files a/frontend/__snapshots__/components-playlist--default--dark.png and b/frontend/__snapshots__/components-playlist--default--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--default--light.png b/frontend/__snapshots__/components-playlist--default--light.png index 40f9f663960a2..38ca665d24d64 100644 Binary files a/frontend/__snapshots__/components-playlist--default--light.png and b/frontend/__snapshots__/components-playlist--default--light.png differ diff --git a/frontend/__snapshots__/components-playlist--multiple-sections--dark.png b/frontend/__snapshots__/components-playlist--multiple-sections--dark.png index bb70cad772b92..98a2b91fbfc6d 100644 Binary files a/frontend/__snapshots__/components-playlist--multiple-sections--dark.png and b/frontend/__snapshots__/components-playlist--multiple-sections--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--multiple-sections--light.png b/frontend/__snapshots__/components-playlist--multiple-sections--light.png index 10bbdebec2c12..19d2b358256f8 100644 Binary files a/frontend/__snapshots__/components-playlist--multiple-sections--light.png and b/frontend/__snapshots__/components-playlist--multiple-sections--light.png differ diff --git a/frontend/__snapshots__/components-playlist--with-footer--dark.png b/frontend/__snapshots__/components-playlist--with-footer--dark.png index 78a17bfdf2b8b..468e971365877 100644 Binary files a/frontend/__snapshots__/components-playlist--with-footer--dark.png and b/frontend/__snapshots__/components-playlist--with-footer--dark.png differ diff --git a/frontend/__snapshots__/components-playlist--with-footer--light.png b/frontend/__snapshots__/components-playlist--with-footer--light.png index 40f9f663960a2..38ca665d24d64 100644 Binary files a/frontend/__snapshots__/components-playlist--with-footer--light.png and b/frontend/__snapshots__/components-playlist--with-footer--light.png differ diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--dark.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--dark.png new file mode 100644 index 0000000000000..cb009d55395dd Binary files /dev/null and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--dark.png differ diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--light.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--light.png new file mode 100644 index 0000000000000..4c8ac367ea329 Binary files /dev/null and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-hidden--light.png differ diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--dark.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--dark.png new file mode 100644 index 0000000000000..d8a5618560ff3 Binary files /dev/null and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--dark.png differ diff --git a/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--light.png b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--light.png new file mode 100644 index 0000000000000..88a2b1085bae5 Binary files /dev/null and b/frontend/__snapshots__/components-properties-table--dollar-properties-on-person-searchable--light.png differ diff --git a/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--dark.png b/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--dark.png index e3e93b3dd9678..b54d4facb0bfe 100644 Binary files a/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--dark.png and b/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--dark.png differ diff --git a/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--light.png b/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--light.png index d4377362d9270..332ea24ce3084 100644 Binary files a/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--light.png and b/frontend/__snapshots__/exporter-exporter--trends-line-insight-detailed--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--dark.png b/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--dark.png index cbb31b2fef96d..5b9d724c95a96 100644 Binary files a/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--dark.png and b/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--light.png b/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--light.png index 84d1b755c6ffd..c820ba1044cd8 100644 Binary files a/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--light.png and b/frontend/__snapshots__/lemon-ui-colors--all-three-thousand-color-options--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--dark.png b/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--dark.png index 6dba78ce8a720..9b3f25ef524ce 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--light.png b/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--light.png index 92b06894e5bdd..f24e45c527717 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--light.png and b/frontend/__snapshots__/lemon-ui-lemon-checkbox--bordered--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--dark.png b/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--dark.png index 9d11b77a107be..aadf83a5cca37 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--light.png b/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--light.png index 467fff8f7e1ca..6020cb5ecb1bb 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--light.png and b/frontend/__snapshots__/lemon-ui-lemon-checkbox--overview--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--dark.png b/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--dark.png index 25f23db23c6e6..8fbdfe8b76fef 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--light.png b/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--light.png index 8e192d169ba4f..25d4b1bc1c185 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--light.png and b/frontend/__snapshots__/lemon-ui-lemon-field--fields-with-kea-form--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--dark.png b/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--dark.png index 24eac264804e4..99779f0baae6d 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--light.png b/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--light.png index df046c562eb4f..24cf27496c843 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--light.png and b/frontend/__snapshots__/lemon-ui-lemon-field--pure-fields--light.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--dark.png b/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--dark.png index f271cd3ec278a..50a7c500c0453 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--dark.png differ diff --git a/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--light.png b/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--light.png index e703172e04fa2..966d2e5bd7c3c 100644 Binary files a/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--light.png and b/frontend/__snapshots__/lemon-ui-lemon-progress-circle--overview--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 789d794e15160..e88dccd9f2b55 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__/replay-player-failure--recent-recordings-404--light.png b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png index bb0ee5866829e..3c6d5e12507d6 100644 Binary files a/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png and b/frontend/__snapshots__/replay-player-failure--recent-recordings-404--light.png differ diff --git a/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png b/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png index b2d4c263e841a..102a4203a02af 100644 Binary files a/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png and b/frontend/__snapshots__/replay-player-success--recent-recordings--dark.png differ diff --git a/frontend/__snapshots__/replay-player-success--recent-recordings--light.png b/frontend/__snapshots__/replay-player-success--recent-recordings--light.png index 600363cbe918f..a0cb77b34bb3d 100644 Binary files a/frontend/__snapshots__/replay-player-success--recent-recordings--light.png and b/frontend/__snapshots__/replay-player-success--recent-recordings--light.png differ diff --git a/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png b/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png index 085e1712a86b5..cb2256b059680 100644 Binary files a/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png and b/frontend/__snapshots__/replay-player-success--second-recording-in-list--dark.png differ diff --git a/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png b/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png index 0aca8202e0d92..38266f259b5b3 100644 Binary files a/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png and b/frontend/__snapshots__/replay-player-success--second-recording-in-list--light.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--disabled--dark.png b/frontend/__snapshots__/replay-watch-next-panel--disabled--dark.png new file mode 100644 index 0000000000000..314fc70e79b2c Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--disabled--dark.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--disabled--light.png b/frontend/__snapshots__/replay-watch-next-panel--disabled--light.png new file mode 100644 index 0000000000000..4b77ee8e8e5e3 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--disabled--light.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--empty--dark.png b/frontend/__snapshots__/replay-watch-next-panel--empty--dark.png new file mode 100644 index 0000000000000..aa1dbc4f6b5b5 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--empty--dark.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--empty--light.png b/frontend/__snapshots__/replay-watch-next-panel--empty--light.png new file mode 100644 index 0000000000000..458a79009b242 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--empty--light.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--loading--dark.png b/frontend/__snapshots__/replay-watch-next-panel--loading--dark.png new file mode 100644 index 0000000000000..19ee177f1ef86 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--loading--dark.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--loading--light.png b/frontend/__snapshots__/replay-watch-next-panel--loading--light.png new file mode 100644 index 0000000000000..90c47df650fca Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--loading--light.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--scores--dark.png b/frontend/__snapshots__/replay-watch-next-panel--scores--dark.png new file mode 100644 index 0000000000000..d415312219653 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--scores--dark.png differ diff --git a/frontend/__snapshots__/replay-watch-next-panel--scores--light.png b/frontend/__snapshots__/replay-watch-next-panel--scores--light.png new file mode 100644 index 0000000000000..037153a091079 Binary files /dev/null and b/frontend/__snapshots__/replay-watch-next-panel--scores--light.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 0d0cd6d752a46..6afb89461f228 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-insights--funnel-top-to-bottom-breakdown-edit--dark.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png index 0725eb97f8df0..5a2eb06823ab6 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png index 74e8409c16b8b..78ea79ea3f745 100644 Binary files a/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png and b/frontend/__snapshots__/scenes-app-insights--funnel-top-to-bottom-breakdown-edit--light.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png index 6495b842d4341..726824db9df72 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png index a74d32c607583..af4459e63b7a4 100644 Binary files a/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png and b/frontend/__snapshots__/scenes-app-notebooks--recordings-playlist--light.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png index 7bdac43e78a6f..820998e556ba5 100644 Binary files a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png index 533a2c0833692..e7ec3a315684b 100644 Binary files a/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png and b/frontend/__snapshots__/scenes-app-sessionattributionexplorer--session-attribution-explorer--light.png differ diff --git a/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--dark.png b/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--dark.png index 4fe845177fd6b..29fe5a51e80b4 100644 Binary files a/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--dark.png and b/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--dark.png differ diff --git a/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--light.png b/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--light.png index d4cf49f5c030a..71f9dac33c3cd 100644 Binary files a/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--light.png and b/frontend/__snapshots__/scenes-app-sidepanels--side-panel-docs--light.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png index f1495e24cf60f..83009eedca75c 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png and b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--light.png b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--light.png index e751008bc5d59..1ef92fbabedfa 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--light.png and b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal--light.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--dark.png b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--dark.png index bb709a5c87410..b646b463af3f0 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--dark.png and b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--light.png b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--light.png index 3bda7cb85e52f..475c2c0eff0ef 100644 Binary files a/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--light.png and b/frontend/__snapshots__/scenes-other-billing--billing-unsubscribe-modal-data-pipelines--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--actions--dark.png b/frontend/__snapshots__/scenes-other-toolbar--actions--dark.png index 7d16576f50ed0..5f6b33fe06f12 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--actions--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--actions--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--actions--light.png b/frontend/__snapshots__/scenes-other-toolbar--actions--light.png index cd1ecee7c7a46..f202e5abcbe2a 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--actions--light.png and b/frontend/__snapshots__/scenes-other-toolbar--actions--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--actions-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--actions-dark--dark.png index f1505fe48b29d..7d66b287789ad 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--actions-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--actions-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--actions-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--actions-dark--light.png index 5ac59acf6dd8e..bf0e04794518a 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--actions-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--actions-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--default--dark.png b/frontend/__snapshots__/scenes-other-toolbar--default--dark.png index c12c2d0aef813..89a609253c9e4 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--default--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--default--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--default--light.png b/frontend/__snapshots__/scenes-other-toolbar--default--light.png index caa5c3f31823f..e2f6298e7900c 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--default--light.png and b/frontend/__snapshots__/scenes-other-toolbar--default--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--default-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--default-dark--dark.png index 1c515a3374e43..0b1ba9d5b38bb 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--default-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--default-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--default-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--default-dark--light.png index a4114f85ddcbd..24149042213d2 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--default-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--default-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--dark.png b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--dark.png index 523eb19bf4797..958c3a8180f92 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--light.png b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--light.png index 84fcc60d37928..113aa274ef69d 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--light.png and b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--dark.png index b0a5521927ba7..bb3710e29705f 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--light.png index b3593cbdd1a96..6a3cce109cc6a 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--events-debugger-empty-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--experiments--dark.png b/frontend/__snapshots__/scenes-other-toolbar--experiments--dark.png index 74ebee5b45dc5..5bf2d673e126b 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--experiments--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--experiments--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--experiments--light.png b/frontend/__snapshots__/scenes-other-toolbar--experiments--light.png index bd3fbbca1ccd7..75498c2dc471e 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--experiments--light.png and b/frontend/__snapshots__/scenes-other-toolbar--experiments--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--feature-flags--dark.png b/frontend/__snapshots__/scenes-other-toolbar--feature-flags--dark.png index 39cf734a11dfc..5428653edc4f6 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--feature-flags--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--feature-flags--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--feature-flags--light.png b/frontend/__snapshots__/scenes-other-toolbar--feature-flags--light.png index a648d688dbff8..eb6543dcb4c82 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--feature-flags--light.png and b/frontend/__snapshots__/scenes-other-toolbar--feature-flags--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--dark.png index 035dcb567bafb..e46c1cd5c8a59 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--light.png index e6bd850589912..0d27d842b1a7e 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--feature-flags-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--heatmap--dark.png b/frontend/__snapshots__/scenes-other-toolbar--heatmap--dark.png index f608cfb82aff9..532363bec2e0e 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--heatmap--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--heatmap--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--heatmap--light.png b/frontend/__snapshots__/scenes-other-toolbar--heatmap--light.png index cfb3647b74fb1..91387589ac54f 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--heatmap--light.png and b/frontend/__snapshots__/scenes-other-toolbar--heatmap--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--dark.png index 78ecb45e93619..844bb2ab7b167 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--light.png index c50119609512c..40803fcdd4478 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--heatmap-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--inspect--dark.png b/frontend/__snapshots__/scenes-other-toolbar--inspect--dark.png index 5c394c8d47b19..1807cec97d98f 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--inspect--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--inspect--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--inspect--light.png b/frontend/__snapshots__/scenes-other-toolbar--inspect--light.png index 6f726cd8f063c..4f2e4aee2033d 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--inspect--light.png and b/frontend/__snapshots__/scenes-other-toolbar--inspect--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--dark.png index 9a0c3228aa9e5..ea3543905b229 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--light.png index 33018ee4116aa..6818c6b7b16b1 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--inspect-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--minimized--dark.png b/frontend/__snapshots__/scenes-other-toolbar--minimized--dark.png index 4293ac7a888c2..5e191ec165bb0 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--minimized--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--minimized--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--minimized--light.png b/frontend/__snapshots__/scenes-other-toolbar--minimized--light.png index 6cef713183099..4e21e542cad79 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--minimized--light.png and b/frontend/__snapshots__/scenes-other-toolbar--minimized--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--dark.png b/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--dark.png index 8215d29cf6f50..24b9ca2c243e5 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--light.png b/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--light.png index c52d993540a00..7da3a3b0bb887 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--light.png and b/frontend/__snapshots__/scenes-other-toolbar--minimized-dark--light.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--dark.png b/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--dark.png index 4ed719d8732c4..61d74438e5157 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--dark.png and b/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--dark.png differ diff --git a/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--light.png b/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--light.png index e45d7d9e63a16..631c297ebb36d 100644 Binary files a/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--light.png and b/frontend/__snapshots__/scenes-other-toolbar--unauthenticated--light.png differ diff --git a/frontend/public/services/discord.png b/frontend/public/services/discord.png new file mode 100644 index 0000000000000..d21ba297b0d9c Binary files /dev/null and b/frontend/public/services/discord.png differ diff --git a/frontend/public/services/mailchimp.png b/frontend/public/services/mailchimp.png new file mode 100644 index 0000000000000..d57818566818b Binary files /dev/null and b/frontend/public/services/mailchimp.png differ diff --git a/frontend/public/services/microsoft-teams.png b/frontend/public/services/microsoft-teams.png new file mode 100644 index 0000000000000..8c06355885650 Binary files /dev/null and b/frontend/public/services/microsoft-teams.png differ diff --git a/frontend/src/lib/actionUtils.ts b/frontend/src/lib/actionUtils.ts index 87afffa1a0c4e..9096490300987 100644 --- a/frontend/src/lib/actionUtils.ts +++ b/frontend/src/lib/actionUtils.ts @@ -20,7 +20,6 @@ export const EXPERIMENT_TARGETS = [ 'del', 'details', 'dfn', - 'div', 'footer', 'header', 'ol', diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 6cd37102ae2e7..f8adb51e539a5 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -480,12 +480,12 @@ class ApiRequest { } // Recordings - public recording(recordingId: SessionRecordingType['id'], teamId?: TeamType['id']): ApiRequest { - return this.environmentsDetail(teamId).addPathComponent('session_recordings').addPathComponent(recordingId) - } public recordings(teamId?: TeamType['id']): ApiRequest { return this.environmentsDetail(teamId).addPathComponent('session_recordings') } + public recording(recordingId: SessionRecordingType['id'], teamId?: TeamType['id']): ApiRequest { + return this.recordings(teamId).addPathComponent(recordingId) + } public recordingMatchingEvents(teamId?: TeamType['id']): ApiRequest { return this.environmentsDetail(teamId) .addPathComponent('session_recordings') @@ -748,7 +748,7 @@ class ApiRequest { } public subscription(id: SubscriptionType['id'], teamId?: TeamType['id']): ApiRequest { - return this.environmentsDetail(teamId).addPathComponent(id) + return this.subscriptions(teamId).addPathComponent(id) } // # Integrations @@ -2083,6 +2083,13 @@ const api = { ): Promise { return await new ApiRequest().batchExportRun(id, runId, teamId).withAction('retry').create() }, + async cancelRun( + id: BatchExportConfiguration['id'], + runId: BatchExportRun['id'], + teamId?: TeamType['id'] + ): Promise { + return await new ApiRequest().batchExportRun(id, runId, teamId).withAction('cancel').create() + }, async logs( id: BatchExportConfiguration['id'], params: LogEntryRequestParams = {} diff --git a/frontend/src/lib/components/Alerts/insightAlertsLogic.ts b/frontend/src/lib/components/Alerts/insightAlertsLogic.ts index 5ef92b84b8de2..f95c941eb3896 100644 --- a/frontend/src/lib/components/Alerts/insightAlertsLogic.ts +++ b/frontend/src/lib/components/Alerts/insightAlertsLogic.ts @@ -35,6 +35,7 @@ export const insightAlertsLogic = kea([ connect((props: InsightAlertsLogicProps) => ({ actions: [insightVizDataLogic(props.insightLogicProps), ['setQuery']], + values: [insightVizDataLogic(props.insightLogicProps), ['showAlertThresholdLines']], })), loaders(({ props }) => ({ @@ -62,10 +63,11 @@ export const insightAlertsLogic = kea([ selectors({ alertThresholdLines: [ - (s) => [s.alerts], - (alerts: AlertType[]): GoalLine[] => + (s) => [s.alerts, s.showAlertThresholdLines], + (alerts: AlertType[], showAlertThresholdLines: boolean): GoalLine[] => alerts.flatMap((alert) => { if ( + !showAlertThresholdLines || alert.threshold.configuration.type !== InsightThresholdType.ABSOLUTE || alert.condition.type !== AlertConditionType.ABSOLUTE_VALUE || !alert.threshold.configuration.bounds @@ -76,14 +78,14 @@ export const insightAlertsLogic = kea([ const bounds = alert.threshold.configuration.bounds const thresholds = [] - if (bounds?.upper !== undefined) { + if (bounds?.upper != null) { thresholds.push({ label: `${alert.name} Upper Threshold`, value: bounds?.upper, }) } - if (bounds?.lower !== undefined) { + if (bounds?.lower != null) { thresholds.push({ label: `${alert.name} Lower Threshold`, value: bounds?.lower, diff --git a/frontend/src/lib/components/Alerts/views/Alerts.tsx b/frontend/src/lib/components/Alerts/views/Alerts.tsx index 004a848377edb..4bb1129fd1146 100644 --- a/frontend/src/lib/components/Alerts/views/Alerts.tsx +++ b/frontend/src/lib/components/Alerts/views/Alerts.tsx @@ -3,6 +3,7 @@ import { IconCheck } from '@posthog/icons' import { Tooltip } from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' import { router } from 'kea-router' +import { FeedbackNotice } from 'lib/components/FeedbackNotice' import { DetectiveHog } from 'lib/components/hedgehogs' import { ProductIntroduction } from 'lib/components/ProductIntroduction/ProductIntroduction' import { LemonTable, LemonTableColumn, LemonTableColumns } from 'lib/lemon-ui/LemonTable' @@ -104,6 +105,8 @@ export function Alerts({ alertId }: AlertsProps): JSX.Element { // TODO: add info here to sign up for alerts early access return ( <> + + {alertsSortedByState.length === 0 && !alertsLoading && (
-

+

{title}

{viewAllURL && View all} diff --git a/frontend/src/lib/components/Playlist/Playlist.tsx b/frontend/src/lib/components/Playlist/Playlist.tsx index d7cc5ba845b4e..3d720f1f146e2 100644 --- a/frontend/src/lib/components/Playlist/Playlist.tsx +++ b/frontend/src/lib/components/Playlist/Playlist.tsx @@ -44,6 +44,7 @@ export type PlaylistProps = { onChangeSections?: (activeKeys: string[]) => void 'data-attr'?: string activeItemId?: string + controls?: JSX.Element | null } const CounterBadge = ({ children }: { children: React.ReactNode }): JSX.Element => ( @@ -70,6 +71,7 @@ export function Playlist< onSelect, onChangeSections, 'data-attr': dataAttr, + controls, }: PlaylistProps): JSX.Element { const [controlledActiveItemId, setControlledActiveItemId] = useState( selectInitialItem && sections[0].items[0] ? sections[0].items[0].id : null @@ -115,6 +117,7 @@ export function Playlist< setActiveItemId={onChangeActiveItem} onChangeSections={onChangeSections} emptyState={listEmptyState} + controls={controls} /> )} ['title'] notebooksHref: PlaylistProps['notebooksHref'] @@ -166,6 +170,7 @@ function List< onScrollListEdge: PlaylistProps['onScrollListEdge'] loading: PlaylistProps['loading'] emptyState: PlaylistProps['listEmptyState'] + controls?: JSX.Element | null }): JSX.Element { const [activeHeaderActionKey, setActiveHeaderActionKey] = useState(null) const lastScrollPositionRef = useRef(0) @@ -203,42 +208,49 @@ function List< return (
-
- } onClick={onClickCollapse} /> - - {title ? ( - - {title} - - ) : null} - - Showing {itemsCount} results. -
- Scrolling to the bottom or the top of the list will load older or newer results - respectively. - - } - > - - {Math.min(999, itemsCount)}+ - -
-
- {headerActions.map(({ key, icon, tooltip, children }) => ( +
+
setActiveHeaderActionKey(activeHeaderActionKey === key ? null : key)} - > - {children} - - ))} + icon={} + onClick={onClickCollapse} + /> + + {title ? ( + + {title} + + ) : null} + + Showing {itemsCount} results. +
+ Scrolling to the bottom or the top of the list will load older or newer results + respectively. + + } + > + + {Math.min(999, itemsCount)}+ + +
+
+ {headerActions.map(({ key, icon, tooltip, children }) => ( + setActiveHeaderActionKey(activeHeaderActionKey === key ? null : key)} + > + {children} + + ))} +
+ {controls ?
{controls}
: null}
diff --git a/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx b/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx index 440000f5200b5..9f2ab2f83704f 100644 --- a/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx +++ b/frontend/src/lib/components/PropertiesTable/PropertiesTable.stories.tsx @@ -1,4 +1,7 @@ import { Meta, StoryFn } from '@storybook/react' +import { useActions } from 'kea' +import { userPreferencesLogic } from 'lib/logic/userPreferencesLogic' +import { useEffect } from 'react' import { PropertyDefinitionType } from '~/types' @@ -46,7 +49,7 @@ export const DollarPropertiesOnEvent: StoryFn = () => { return } -export const DollarPropertiesOnPerson: StoryFn = () => { +export const DollarPropertiesOnPersonSearchable: StoryFn = () => { const properties = { pineapple_enjoyment_score: 3, $browser: 'Chrome', @@ -56,5 +59,24 @@ export const DollarPropertiesOnPerson: StoryFn = () => { $initial_utm_campaign: 'summer_sale', $initial_geoip_country_code: 'US', } - return + return +} + +export const DollarPropertiesOnPersonHidden: StoryFn = () => { + const { setHidePostHogPropertiesInTable } = useActions(userPreferencesLogic) + + useEffect(() => setHidePostHogPropertiesInTable(true), []) + + const properties = { + pineapple_enjoyment_score: 3, + $browser: 'Chrome', + utm_campaign: 'summer_sale', + $geoip_country_code: 'US', + $initial_browser: 'Chrome', + $initial_utm_campaign: 'summer_sale', + $initial_geoip_country_code: 'US', + } + return ( + + ) } diff --git a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx index b245d5db8bae4..296317e5d7e7b 100644 --- a/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx +++ b/frontend/src/lib/components/PropertiesTable/PropertiesTable.tsx @@ -9,9 +9,9 @@ import { LemonButton } from 'lib/lemon-ui/LemonButton' import { LemonTable, LemonTableColumns, LemonTableProps } from 'lib/lemon-ui/LemonTable' import { userPreferencesLogic } from 'lib/logic/userPreferencesLogic' import { + CLOUD_INTERNAL_POSTHOG_PROPERTY_KEYS, CORE_FILTER_DEFINITIONS_BY_GROUP, getCoreFilterDefinition, - NON_DOLLAR_POSTHOG_PROPERTY_KEYS, PROPERTY_KEYS, } from 'lib/taxonomy' import { isURL } from 'lib/utils' @@ -249,8 +249,8 @@ export function PropertiesTable({ if (filterable && hidePostHogPropertiesInTable) { entries = entries.filter(([key]) => { - const isPostHogProperty = key.startsWith('$') && PROPERTY_KEYS.includes(key) - const isNonDollarPostHogProperty = isCloudOrDev && NON_DOLLAR_POSTHOG_PROPERTY_KEYS.includes(key) + const isPostHogProperty = key.startsWith('$') || PROPERTY_KEYS.includes(key) + const isNonDollarPostHogProperty = isCloudOrDev && CLOUD_INTERNAL_POSTHOG_PROPERTY_KEYS.includes(key) return !isPostHogProperty && !isNonDollarPostHogProperty }) } @@ -410,9 +410,10 @@ export function PropertiesTable({ {searchable && ( )} diff --git a/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts b/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts index 1adc197e2c03c..4af30359d6422 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionLogic.test.ts @@ -26,12 +26,11 @@ export const fixtureSubscriptionResponse = (id: number, args: Partial { let newLogic: ReturnType let existingLogic: ReturnType - let subscriptions: SubscriptionType[] = [] beforeEach(async () => { - subscriptions = [fixtureSubscriptionResponse(1), fixtureSubscriptionResponse(2)] useMocks({ get: { - '/api/projects/:team/subscriptions/1': fixtureSubscriptionResponse(1), + '/api/environments/:team/subscriptions': { count: 1, results: [fixtureSubscriptionResponse(1)] }, + '/api/environments/:team/subscriptions/1': fixtureSubscriptionResponse(1), '/api/projects/:team/integrations': { count: 0, results: [] }, }, }) @@ -42,12 +41,28 @@ describe('subscriptionLogic', () => { }) existingLogic = subscriptionLogic({ insightShortId: Insight1, - id: subscriptions[0].id, + id: 1, }) newLogic.mount() existingLogic.mount() }) + it('loads existing subscription', async () => { + router.actions.push('/insights/123/subscriptions/1') + await expectLogic(existingLogic).toFinishListeners().toDispatchActions(['loadSubscriptionSuccess']) + expect(existingLogic.values.subscription).toMatchObject({ + id: 1, + title: 'My example subscription', + target_type: 'email', + target_value: 'ben@posthog.com,geoff@other-company.com', + frequency: 'monthly', + interval: 2, + start_date: '2022-01-01T00:09:00', + byweekday: ['wednesday'], + bysetpos: 1, + }) + }) + it('updates values depending on frequency', async () => { router.actions.push('/insights/123/subscriptions/new') await expectLogic(newLogic).toFinishListeners() diff --git a/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts b/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts index a3b328435be0f..c3a2564efdeee 100644 --- a/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts +++ b/frontend/src/lib/components/Subscriptions/subscriptionLogic.ts @@ -1,4 +1,4 @@ -import { connect, kea, key, listeners, path, props } from 'kea' +import { kea, key, listeners, path, props } from 'kea' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' import { beforeUnload, router, urlToAction } from 'kea-router' @@ -30,9 +30,6 @@ export const subscriptionLogic = kea([ path(['lib', 'components', 'Subscriptions', 'subscriptionLogic']), props({} as SubscriptionsLogicProps), key(({ id, insightShortId, dashboardId }) => `${insightShortId || dashboardId}-${id ?? 'new'}`), - connect(({ insightShortId, dashboardId }: SubscriptionsLogicProps) => ({ - actions: [subscriptionsLogic({ insightShortId, dashboardId }), ['loadSubscriptions']], - })), loaders(({ props }) => ({ subscription: { @@ -97,7 +94,9 @@ export const subscriptionLogic = kea([ router.actions.replace(urlForSubscription(updatedSub.id, props)) } - actions.loadSubscriptions() + // If a subscriptionsLogic for this insight/dashboard is mounted already, let's make sure + // this change is propagated to `subscriptions` there + subscriptionsLogic.findMounted(props)?.actions.loadSubscriptions() actions.loadSubscriptionSuccess(updatedSub) lemonToast.success(`Subscription saved.`) diff --git a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx index c65ffd600898f..3d5973f55d75e 100644 --- a/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/TaxonomicFilter.tsx @@ -36,6 +36,7 @@ export function TaxonomicFilter({ selectFirstItem = true, propertyAllowList, hideBehavioralCohorts, + showNumericalPropsOnly, }: TaxonomicFilterProps): JSX.Element { // Generate a unique key for each unique TaxonomicFilter that's rendered const taxonomicFilterLogicKey = useMemo( @@ -62,6 +63,7 @@ export function TaxonomicFilter({ metadataSource, propertyAllowList, hideBehavioralCohorts, + showNumericalPropsOnly, } const logic = taxonomicFilterLogic(taxonomicFilterLogicProps) diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts index 25e2866afc2c3..bf9b55115cb69 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.test.ts @@ -66,6 +66,7 @@ describe('infiniteListLogic', () => { taxonomicFilterLogicKey: 'testList', listGroupType: TaxonomicFilterGroupType.Events, taxonomicGroupTypes: [TaxonomicFilterGroupType.Events], + showNumericalPropsOnly: false, } const logicWithProps = infiniteListLogic({ ...defaultProps, ...props }) logicWithProps.mount() @@ -98,6 +99,7 @@ describe('infiniteListLogic', () => { taxonomicFilterLogicKey: 'testList', listGroupType: TaxonomicFilterGroupType.Events, taxonomicGroupTypes: [TaxonomicFilterGroupType.Events], + showNumericalPropsOnly: false, }) logic.mount() }) @@ -180,11 +182,11 @@ describe('infiniteListLogic', () => { index: 0, remoteItems: partial({ count: 156 }), localItems: partial({ count: 1 }), - items: partial({ count: 157 }), + items: partial({ count: 101 }), }) - expectLogic(logic, () => logic.actions.moveUp()).toMatchValues({ index: 156 }) - expectLogic(logic, () => logic.actions.moveUp()).toMatchValues({ index: 155 }) - expectLogic(logic, () => logic.actions.moveDown()).toMatchValues({ index: 156 }) + expectLogic(logic, () => logic.actions.moveUp()).toMatchValues({ index: 100 }) + expectLogic(logic, () => logic.actions.moveUp()).toMatchValues({ index: 99 }) + expectLogic(logic, () => logic.actions.moveDown()).toMatchValues({ index: 100 }) expectLogic(logic, () => logic.actions.moveDown()).toMatchValues({ index: 0 }) expectLogic(logic, () => logic.actions.moveDown()).toMatchValues({ index: 1 }) expectLogic(logic, () => logic.actions.moveUp()).toMatchValues({ index: 0 }) @@ -196,7 +198,7 @@ describe('infiniteListLogic', () => { index: 0, remoteItems: partial({ count: 156 }), localItems: partial({ count: 1 }), - items: partial({ count: 157 }), + items: partial({ count: 101 }), }) await expectLogic(logic, () => @@ -237,6 +239,7 @@ describe('infiniteListLogic', () => { taxonomicFilterLogicKey: 'testList', listGroupType: TaxonomicFilterGroupType.Wildcards, taxonomicGroupTypes: [TaxonomicFilterGroupType.Events, TaxonomicFilterGroupType.Actions], + showNumericalPropsOnly: false, optionsFromProp: { wildcard: [{ name: 'first' }, { name: 'second' }], }, @@ -260,6 +263,7 @@ describe('infiniteListLogic', () => { listGroupType: TaxonomicFilterGroupType.EventProperties, eventNames: ['$pageview'], taxonomicGroupTypes: [TaxonomicFilterGroupType.EventProperties], + showNumericalPropsOnly: false, }) logic.mount() }) @@ -368,9 +372,9 @@ describe('infiniteListLogic', () => { isExpandable: false, isExpanded: true, isExpandableButtonSelected: false, - totalResultCount: 2, + totalResultCount: 3, totalExtraCount: 0, - totalListCount: 2, + totalListCount: 3, expandedCount: 0, remoteItems: partial({ count: 2, @@ -389,6 +393,7 @@ describe('infiniteListLogic', () => { taxonomicFilterLogicKey: 'test-element-list', listGroupType: TaxonomicFilterGroupType.Elements, taxonomicGroupTypes: [TaxonomicFilterGroupType.Elements], + showNumericalPropsOnly: false, }) logicWithProps.mount() diff --git a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts index eb8ca78c45ec4..05c5a387cad0f 100644 --- a/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts +++ b/frontend/src/lib/components/TaxonomicFilter/infiniteListLogic.ts @@ -69,7 +69,7 @@ async function fetchCachedListResponse(path: string, searchParams: Record([ - props({} as InfiniteListLogicProps), + props({ showNumericalPropsOnly: false } as InfiniteListLogicProps), key((props) => `${props.taxonomicFilterLogicKey}-${props.listGroupType}`), path((key) => ['lib', 'components', 'TaxonomicFilter', 'infiniteListLogic', key]), connect((props: InfiniteListLogicProps) => ({ @@ -310,15 +310,34 @@ export const infiniteListLogic = kea([ }, ], items: [ - (s) => [s.remoteItems, s.localItems], - (remoteItems, localItems) => ({ - results: [...localItems.results, ...remoteItems.results], - count: localItems.count + remoteItems.count, - searchQuery: localItems.searchQuery, - expandedCount: remoteItems.expandedCount, - queryChanged: remoteItems.queryChanged, - first: localItems.first && remoteItems.first, - }), + (s, p) => [s.remoteItems, s.localItems, p.showNumericalPropsOnly ?? (() => false)], + (remoteItems, localItems, showNumericalPropsOnly) => { + const results = [...localItems.results, ...remoteItems.results].filter((n) => { + if (!showNumericalPropsOnly) { + return true + } + + if ('is_numerical' in n) { + return !!n.is_numerical + } + + if ('property_type' in n) { + const property_type = n.property_type as string // Data warehouse props dont conformt to PropertyType for some reason + return property_type === 'Integer' || property_type === 'Float' + } + + return true + }) + + return { + results, + count: results.length, + searchQuery: localItems.searchQuery, + expandedCount: remoteItems.expandedCount, + queryChanged: remoteItems.queryChanged, + first: localItems.first && remoteItems.first, + } + }, ], totalResultCount: [(s) => [s.items], (items) => items.count || 0], totalExtraCount: [ diff --git a/frontend/src/lib/components/TaxonomicFilter/types.ts b/frontend/src/lib/components/TaxonomicFilter/types.ts index 40931c4ef93e3..8bbfdb247a607 100644 --- a/frontend/src/lib/components/TaxonomicFilter/types.ts +++ b/frontend/src/lib/components/TaxonomicFilter/types.ts @@ -38,6 +38,7 @@ export interface TaxonomicFilterProps { propertyAllowList?: { [key in TaxonomicFilterGroupType]?: string[] } // only return properties in this list, currently only working for EventProperties and PersonProperties metadataSource?: AnyDataNode hideBehavioralCohorts?: boolean + showNumericalPropsOnly?: boolean } export interface TaxonomicFilterLogicProps extends TaxonomicFilterProps { diff --git a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx index c7f9adf4ac81a..2f80f363b674d 100644 --- a/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx +++ b/frontend/src/lib/components/TaxonomicPopover/TaxonomicPopover.tsx @@ -26,6 +26,7 @@ export interface TaxonomicPopoverProps): JSX.Element { const [localValue, setLocalValue] = useState(value || ('' as ValueType)) @@ -94,6 +96,7 @@ export function TaxonomicPopover } matchWidth={false} diff --git a/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx b/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx index 049b4cb3da0ee..5b8c367f8291b 100644 --- a/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx +++ b/frontend/src/lib/components/UniversalFilters/UniversalFilterButton.tsx @@ -11,12 +11,11 @@ import React from 'react' import { cohortsModel } from '~/models/cohortsModel' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { ActionFilter, AnyPropertyFilter } from '~/types' +import { ActionFilter, AnyPropertyFilter, FeaturePropertyFilter, UniversalFilterValue } from '~/types' import { EntityFilterInfo } from '../EntityFilterInfo' import { formatPropertyLabel } from '../PropertyFilters/utils' -import { UniversalFilterValue } from './UniversalFilters' -import { isActionFilter, isEditableFilter, isEventFilter } from './utils' +import { isActionFilter, isEditableFilter, isEventFilter, isFeatureFlagFilter } from './utils' export interface UniversalFilterButtonProps { onClick?: () => void @@ -33,7 +32,7 @@ export const UniversalFilterButton = React.forwardRef ) : isAction ? ( + ) : isFeatureFlag ? ( + ) : ( )} @@ -116,3 +117,7 @@ const EventLabel = ({
) } + +const FeatureFlagLabel = ({ filter }: { filter: FeaturePropertyFilter }): JSX.Element => { + return
{filter.key}
+} diff --git a/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx b/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx index b59471de24cc0..117c6b678c59e 100644 --- a/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx +++ b/frontend/src/lib/components/UniversalFilters/UniversalFilters.tsx @@ -3,7 +3,7 @@ import { LemonButton, LemonButtonProps, LemonDropdown, Popover } from '@posthog/ import { BindLogic, useActions, useValues } from 'kea' import { useState } from 'react' -import { ActionFilter, AnyPropertyFilter, FilterLogicalOperator } from '~/types' +import { UniversalFiltersGroup, UniversalFilterValue } from '~/types' import { TaxonomicPropertyFilter } from '../PropertyFilters/components/TaxonomicPropertyFilter' import { PropertyFilters } from '../PropertyFilters/PropertyFilters' @@ -14,14 +14,6 @@ import { UniversalFilterButton } from './UniversalFilterButton' import { universalFiltersLogic } from './universalFiltersLogic' import { isEditableFilter, isEventFilter } from './utils' -export interface UniversalFiltersGroup { - type: FilterLogicalOperator - values: UniversalFiltersGroupValue[] -} - -export type UniversalFiltersGroupValue = UniversalFiltersGroup | UniversalFilterValue -export type UniversalFilterValue = AnyPropertyFilter | ActionFilter - type UniversalFiltersProps = { rootKey: string group: UniversalFiltersGroup | null @@ -160,7 +152,7 @@ const AddFilterButton = (props: Omit setDropdownOpen(!dropdownOpen)} {...props} > - Add filter + {props?.title || 'Add filter'} ) diff --git a/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.test.ts b/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.test.ts index 82b52b2e38053..5434ff37f76e1 100644 --- a/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.test.ts +++ b/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.test.ts @@ -1,10 +1,15 @@ import { expectLogic } from 'kea-test-utils' import { initKeaTests } from '~/test/init' -import { AnyPropertyFilter, FilterLogicalOperator, PropertyFilterType, PropertyOperator } from '~/types' +import { + AnyPropertyFilter, + FilterLogicalOperator, + PropertyFilterType, + PropertyOperator, + UniversalFiltersGroup, +} from '~/types' import { TaxonomicFilterGroup, TaxonomicFilterGroupType } from '../TaxonomicFilter/types' -import { UniversalFiltersGroup } from './UniversalFilters' import { universalFiltersLogic } from './universalFiltersLogic' const propertyFilter: AnyPropertyFilter = { diff --git a/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.ts b/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.ts index 8afb3cfcfc1cc..943688c46f3b7 100644 --- a/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.ts +++ b/frontend/src/lib/components/UniversalFilters/universalFiltersLogic.ts @@ -6,10 +6,17 @@ import { import { taxonomicFilterGroupTypeToEntityType } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' -import { ActionFilter, FilterLogicalOperator, PropertyFilterType } from '~/types' +import { + ActionFilter, + FeaturePropertyFilter, + FilterLogicalOperator, + PropertyFilterType, + PropertyOperator, + UniversalFiltersGroup, + UniversalFiltersGroupValue, +} from '~/types' import { TaxonomicFilterGroup, TaxonomicFilterGroupType, TaxonomicFilterValue } from '../TaxonomicFilter/types' -import { UniversalFiltersGroup, UniversalFiltersGroupValue } from './UniversalFilters' import type { universalFiltersLogicType } from './universalFiltersLogicType' export const DEFAULT_UNIVERSAL_GROUP_FILTER: UniversalFiltersGroup = { @@ -52,7 +59,7 @@ export const universalFiltersLogic = kea([ addGroupFilter: ( taxonomicGroup: TaxonomicFilterGroup, propertyKey: TaxonomicFilterValue, - item: { propertyFilterType?: PropertyFilterType; name?: string } + item: { propertyFilterType?: PropertyFilterType; name?: string; key?: string } ) => ({ taxonomicGroup, propertyKey, @@ -98,6 +105,7 @@ export const universalFiltersLogic = kea([ TaxonomicFilterGroupType.Cohorts, TaxonomicFilterGroupType.Elements, TaxonomicFilterGroupType.HogQLExpression, + TaxonomicFilterGroupType.FeatureFlags, ].includes(t) ), ], @@ -112,26 +120,39 @@ export const universalFiltersLogic = kea([ addGroupFilter: ({ taxonomicGroup, propertyKey, item }) => { const newValues = [...values.filterGroup.values] - const propertyType = item.propertyFilterType ?? taxonomicFilterTypeToPropertyFilterType(taxonomicGroup.type) - if (propertyKey && propertyType) { - const newPropertyFilter = createDefaultPropertyFilter( - {}, - propertyKey, - propertyType, - taxonomicGroup, - values.describeProperty - ) - newValues.push(newPropertyFilter) + if (taxonomicGroup.type === TaxonomicFilterGroupType.FeatureFlags) { + if (!item.key) { + return + } + const newFeatureFlagFilter: FeaturePropertyFilter = { + type: PropertyFilterType.Feature, + key: item.key, + operator: PropertyOperator.Exact, + } + newValues.push(newFeatureFlagFilter) } else { - const entityType = taxonomicFilterGroupTypeToEntityType(taxonomicGroup.type) - if (entityType) { - const newEntityFilter: ActionFilter = { - id: propertyKey, - name: item?.name ?? '', - type: entityType, - } + const propertyType = + item.propertyFilterType ?? taxonomicFilterTypeToPropertyFilterType(taxonomicGroup.type) + if (propertyKey && propertyType) { + const newPropertyFilter = createDefaultPropertyFilter( + {}, + propertyKey, + propertyType, + taxonomicGroup, + values.describeProperty + ) + newValues.push(newPropertyFilter) + } else { + const entityType = taxonomicFilterGroupTypeToEntityType(taxonomicGroup.type) + if (entityType) { + const newEntityFilter: ActionFilter = { + id: propertyKey, + name: item?.name ?? '', + type: entityType, + } - newValues.push(newEntityFilter) + newValues.push(newEntityFilter) + } } } actions.setGroupValues(newValues) diff --git a/frontend/src/lib/components/UniversalFilters/utils.ts b/frontend/src/lib/components/UniversalFilters/utils.ts index f8b63af80ce5e..56eaa52f5f6d9 100644 --- a/frontend/src/lib/components/UniversalFilters/utils.ts +++ b/frontend/src/lib/components/UniversalFilters/utils.ts @@ -1,7 +1,15 @@ -import { ActionFilter, FilterLogicalOperator, LogEntryPropertyFilter, RecordingPropertyFilter } from '~/types' +import { + ActionFilter, + FeaturePropertyFilter, + FilterLogicalOperator, + LogEntryPropertyFilter, + RecordingPropertyFilter, + UniversalFiltersGroup, + UniversalFiltersGroupValue, + UniversalFilterValue, +} from '~/types' import { isCohortPropertyFilter } from '../PropertyFilters/utils' -import { UniversalFiltersGroup, UniversalFiltersGroupValue, UniversalFilterValue } from './UniversalFilters' export function isUniversalGroupFilterLike(filter?: UniversalFiltersGroupValue): filter is UniversalFiltersGroup { return filter?.type === FilterLogicalOperator.And || filter?.type === FilterLogicalOperator.Or @@ -15,6 +23,9 @@ export function isEventFilter(filter: UniversalFilterValue): filter is ActionFil export function isActionFilter(filter: UniversalFilterValue): filter is ActionFilter { return filter.type === 'actions' } +export function isFeatureFlagFilter(filter: UniversalFilterValue): filter is FeaturePropertyFilter { + return filter.type === 'feature' +} export function isRecordingPropertyFilter(filter: UniversalFilterValue): filter is RecordingPropertyFilter { return filter.type === 'recording' } diff --git a/frontend/src/lib/constants.tsx b/frontend/src/lib/constants.tsx index 36db4c50cdc65..acdbae72b8530 100644 --- a/frontend/src/lib/constants.tsx +++ b/frontend/src/lib/constants.tsx @@ -215,11 +215,11 @@ export const FEATURE_FLAGS = { INSIGHT_VARIABLES: 'insight_variables', // owner: @Gilbert09 #team-data-warehouse WEB_EXPERIMENTS: 'web-experiments', // owner: @team-feature-success BIGQUERY_DWH: 'bigquery-dwh', // owner: @Gilbert09 #team-data-warehouse - REPLAY_DEFAULT_SORT_ORDER_EXPERIMENT: 'replay-order-by-experiment', // owner: #team-replay ENVIRONMENTS: 'environments', // owner: @Twixes #team-product-analytics BILLING_PAYMENT_ENTRY_IN_APP: 'billing-payment-entry-in-app', // owner: @zach LEGACY_ACTION_WEBHOOKS: 'legacy-action-webhooks', // owner: @mariusandra #team-cdp SESSION_REPLAY_URL_TRIGGER: 'session-replay-url-trigger', // owner: @richard-better #team-replay + REPLAY_TEMPLATES: 'replay-templates', // owner: @raquelmsmith #team-replay } as const export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS] diff --git a/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx b/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx index 0ac8c31c817ee..c7f86768b3bfe 100644 --- a/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx +++ b/frontend/src/lib/lemon-ui/LemonCard/LemonCard.tsx @@ -1,5 +1,9 @@ import './LemonCard.scss' +import { IconX } from '@posthog/icons' + +import { LemonButton } from '../LemonButton' + export interface LemonCardProps { hoverEffect?: boolean className?: string @@ -7,6 +11,8 @@ export interface LemonCardProps { onClick?: () => void focused?: boolean 'data-attr'?: string + closeable?: boolean + onClose?: () => void } export function LemonCard({ @@ -15,16 +21,31 @@ export function LemonCard({ children, onClick, focused, + closeable, + onClose, ...props }: LemonCardProps): JSX.Element { return (
+ {closeable ? ( +
+ } + onClick={(e) => { + e.stopPropagation() + onClose?.() + }} + type="tertiary" + size="xsmall" + /> +
+ ) : null} {children}
) diff --git a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.scss b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.scss index 6af35383fcc7e..b991fcd6121a3 100644 --- a/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.scss +++ b/frontend/src/lib/lemon-ui/LemonCheckbox/LemonCheckbox.scss @@ -1,4 +1,6 @@ .LemonCheckbox { + --lemon-checkbox-height: calc(2.125rem + 3px); // Medium size button height + button shadow height; + display: flex; align-items: center; width: fit-content; @@ -88,7 +90,7 @@ line-height: 1.4; label { - min-height: 2.5rem; + min-height: var(--lemon-checkbox-height); padding: 0 0.75rem; background: var(--bg-light); border: 1px solid var(--border); diff --git a/frontend/src/lib/lemon-ui/colors.stories.tsx b/frontend/src/lib/lemon-ui/colors.stories.tsx index cb3566640b7a7..f2e87c73528af 100644 --- a/frontend/src/lib/lemon-ui/colors.stories.tsx +++ b/frontend/src/lib/lemon-ui/colors.stories.tsx @@ -63,13 +63,123 @@ const preThousand = [ ] const threeThousand = [ + 'primary', + 'danger-highlight', + 'danger-lighter', + 'danger-light', + 'danger', + 'danger-dark', + 'warning-highlight', + 'warning', + 'warning-dark', + 'highlight', + 'success-highlight', + 'success-light', + 'success', + 'success-dark', + 'muted', + 'muted-alt', + 'mark', + 'white', + 'bg-light', + 'side', + 'mid', + 'border', + 'border-light', + 'border-bold', + 'transparent', + 'link', + // Colors of the PostHog logo + 'brand-blue', + 'brand-red', + 'brand-yellow', + 'brand-key', + + // PostHog 3000 + 'text-3000-light', + 'text-secondary-3000-light', + 'muted-3000-light', + 'trace-3000-light', + 'primary-3000-light', + 'primary-highlight-light', + 'primary-3000-hover-light', + 'primary-3000-active-light', + + 'secondary-3000-light', + 'secondary-3000-hover-light', + 'accent-3000-light', + 'bg-3000-light', + 'border-3000-light', + 'border-bold-3000-light', + 'glass-bg-3000-light', + 'glass-border-3000-light', + + 'link-3000-light', + 'primary-3000-frame-bg-light', + 'primary-3000-button-bg-light', + 'primary-3000-button-border-light', + 'primary-3000-button-border-hover-light', + + 'secondary-3000-frame-bg-light', + 'secondary-3000-button-bg-light', + 'secondary-3000-button-border-light', + 'secondary-3000-button-border-hover-light', + + 'danger-3000-frame-bg-light', + 'danger-3000-button-border-light', + 'danger-3000-button-border-hover-light', + + 'shadow-elevation-3000-light', + 'shadow-elevation-3000-dark', + 'text-3000-dark', + 'text-secondary-3000-dark', + 'muted-3000-dark', + 'trace-3000-dark', + 'primary-3000-dark', + 'primary-highlight-dark', + 'primary-3000-hover-dark', + 'primary-3000-active-dark', + 'primary-alt-highlight-light', + + 'secondary-3000-dark', + 'secondary-3000-hover-dark', + 'accent-3000-dark', + 'bg-3000-dark', + 'border-3000-dark', + 'border-bold-3000-dark', + 'glass-bg-3000-dark', + 'glass-border-3000-dark', + 'link-3000-dark', + + 'primary-3000-frame-bg-dark', + 'primary-3000-button-bg-dark', + 'primary-3000-button-border-dark', + 'primary-3000-button-border-hover-dark', + 'primary-alt-highlight-dark', + + 'secondary-3000-frame-bg-dark', + 'secondary-3000-button-bg-dark', + 'secondary-3000-button-border-dark', + 'secondary-3000-button-border-hover-dark', + + 'danger-3000-frame-bg-dark', + 'danger-3000-button-border-dark', + 'danger-3000-button-border-hover-dark', + + // The derived colors + // `--default` is a pre-3000 alias for "default text color" (`--text-3000` now) + 'default', 'text-3000', + 'text-secondary-3000', 'muted-3000', 'primary-3000', 'secondary-3000', 'secondary-3000-hover', 'accent-3000', 'bg-3000', + 'primary-highlight', + 'primary-alt-highlight', + 'primary-alt', ] export function ColorPalette(): JSX.Element { @@ -147,7 +257,7 @@ export function AllThreeThousandColorOptions(): JSX.Element { render: function RenderColor(color) { return (
-
+
) }, @@ -159,7 +269,7 @@ export function AllThreeThousandColorOptions(): JSX.Element { render: function RenderColor(color) { return (
-
+
) }, diff --git a/frontend/src/lib/taxonomy.tsx b/frontend/src/lib/taxonomy.tsx index 76c60960eec8e..a578556789410 100644 --- a/frontend/src/lib/taxonomy.tsx +++ b/frontend/src/lib/taxonomy.tsx @@ -158,7 +158,7 @@ export const CORE_FILTER_DEFINITIONS_BY_GROUP = { description: 'Automatically captured exceptions from the client Sentry integration', }, $web_vitals: { - label: 'Web vitals', + label: 'Web Vitals', description: 'Automatically captured web vitals data', }, // Mobile SDKs events @@ -1417,7 +1417,7 @@ export const PROPERTY_KEYS = Object.keys(CORE_FILTER_DEFINITIONS_BY_GROUP.event_ * but often more numerous than actual properties set on events and useful to hide * to make those properties discoverable */ -export const NON_DOLLAR_POSTHOG_PROPERTY_KEYS = [ +export const CLOUD_INTERNAL_POSTHOG_PROPERTY_KEYS = [ 'billing_period_end', 'billing_period_start', 'current_amount_usd.data_warehouse', @@ -1443,6 +1443,13 @@ export const NON_DOLLAR_POSTHOG_PROPERTY_KEYS = [ 'custom_limits.product_analytics', 'custom_limits.session_replay', 'custom_limits.surveys', + 'custom_limits_usd.data_warehouse', + 'custom_limits_usd.feature_flags', + 'custom_limits_usd.integrations', + 'custom_limits_usd.platform_and_support', + 'custom_limits_usd.product_analytics', + 'custom_limits_usd.session_replay', + 'custom_limits_usd.surveys', 'free_allocation.data_warehouse', 'free_allocation.feature_flags', 'free_allocation.integrations', diff --git a/frontend/src/queries/Query/Query.tsx b/frontend/src/queries/Query/Query.tsx index 2b5d3284bb2ed..ce5785ea88d9d 100644 --- a/frontend/src/queries/Query/Query.tsx +++ b/frontend/src/queries/Query/Query.tsx @@ -118,7 +118,7 @@ export function Query(props: QueryProps): JSX.Element | null /> ) } else if (isSavedInsightNode(query)) { - component = + component = } else if (isInsightVizNode(query)) { component = ( { const [isPopoverOpen, setPopoverOpen] = useState(false) + // Dont show the popover overlay for list variables not in edit mode + if (!showEditingUI && variable.type === 'List') { + return ( + onChange(variable.id, value)} + options={variable.values.map((n) => ({ label: n, value: n }))} + /> + ) + } + return ( ): TrendsF return objectCleanWithEmpty({ smoothingIntervals: filters.smoothing_intervals, showLegend: filters.show_legend, + showAlertThresholdLines: filters.show_alert_threshold_lines, hiddenLegendIndexes: hiddenLegendKeysToIndexes(filters.hidden_legend_keys), aggregationAxisFormat: filters.aggregation_axis_format, aggregationAxisPrefix: filters.aggregation_axis_prefix, diff --git a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts index 9a8f36c6cb548..89e0a6f3d8e7b 100644 --- a/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts +++ b/frontend/src/queries/nodes/InsightQuery/utils/queryNodeToFilter.ts @@ -1,3 +1,4 @@ +import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types' import { objectClean } from 'lib/utils' import { @@ -53,6 +54,7 @@ export const seriesNodeToFilter = ( // TODO: math is not supported by funnel and lifecycle queries math: node.math, math_property: node.math_property, + math_property_type: node.math_property_type as TaxonomicFilterGroupType, math_hogql: node.math_hogql, math_group_type_index: node.math_group_type_index, properties: node.properties as any, // TODO, diff --git a/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx b/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx index 1e72d999b1dfe..d75685def2228 100644 --- a/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx +++ b/frontend/src/queries/nodes/InsightViz/InsightDisplayConfig.tsx @@ -14,6 +14,7 @@ import { funnelDataLogic } from 'scenes/funnels/funnelDataLogic' import { axisLabel } from 'scenes/insights/aggregationAxisFormat' import { PercentStackViewFilter } from 'scenes/insights/EditorFilters/PercentStackViewFilter' import { ScalePicker } from 'scenes/insights/EditorFilters/ScalePicker' +import { ShowAlertThresholdLinesFilter } from 'scenes/insights/EditorFilters/ShowAlertThresholdLinesFilter' import { ShowLegendFilter } from 'scenes/insights/EditorFilters/ShowLegendFilter' import { ValueOnSeriesFilter } from 'scenes/insights/EditorFilters/ValueOnSeriesFilter' import { InsightDateFilter } from 'scenes/insights/filters/InsightDateFilter' @@ -77,6 +78,7 @@ export function InsightDisplayConfig(): JSX.Element { ...(supportsValueOnSeries ? [{ label: () => }] : []), ...(supportsPercentStackView ? [{ label: () => }] : []), ...(hasLegend ? [{ label: () => }] : []), + { label: () => }, ], }, ] diff --git a/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx b/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx index 9135c32cf5156..f67da5034e64f 100644 --- a/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx +++ b/frontend/src/queries/nodes/SavedInsight/SavedInsight.tsx @@ -12,9 +12,11 @@ import { InsightLogicProps } from '~/types' interface InsightProps { query: SavedInsightNode context?: QueryContext + embedded?: boolean + readOnly?: boolean } -export function SavedInsight({ query: propsQuery, context }: InsightProps): JSX.Element { +export function SavedInsight({ query: propsQuery, context, embedded, readOnly }: InsightProps): JSX.Element { const insightProps: InsightLogicProps = { dashboardItemId: propsQuery.shortId } const { insight, insightLoading } = useValues(insightLogic(insightProps)) const { query: dataQuery } = useValues(insightDataLogic(insightProps)) @@ -29,5 +31,13 @@ export function SavedInsight({ query: propsQuery, context }: InsightProps): JSX. const query = { ...propsQuery, ...dataQuery, full: propsQuery.full } - return + return ( + + ) } diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index a9dc7be1bf476..cdb3c4449d002 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -31,6 +31,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -84,6 +87,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -172,6 +178,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -3986,6 +3995,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -4503,6 +4515,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -4935,6 +4950,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -5641,6 +5659,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -5701,6 +5722,9 @@ "math_property": { "type": "string" }, + "math_property_type": { + "type": "string" + }, "name": { "type": "string" }, @@ -10260,6 +10284,10 @@ { "const": "mouse_activity_count", "type": "string" + }, + { + "const": "activity_score", + "type": "string" } ] }, @@ -10833,6 +10861,10 @@ "active_seconds": { "type": "number" }, + "activity_score": { + "description": "calculated on the backend so that we can sort by it, definition may change over time", + "type": "number" + }, "click_count": { "type": "number" }, @@ -11468,6 +11500,10 @@ }, "type": "array" }, + "showAlertThresholdLines": { + "default": false, + "type": "boolean" + }, "showLabelsOnSeries": { "type": "boolean" }, @@ -11538,6 +11574,9 @@ }, "type": "object" }, + "show_alert_threshold_lines": { + "type": "boolean" + }, "show_labels_on_series": { "type": "boolean" }, diff --git a/frontend/src/queries/schema.ts b/frontend/src/queries/schema.ts index 1887f57ee0f96..b288c4f49a364 100644 --- a/frontend/src/queries/schema.ts +++ b/frontend/src/queries/schema.ts @@ -331,6 +331,7 @@ export interface RecordingsQuery extends DataNode { | 'click_count' | 'keypress_count' | 'mouse_activity_count' + | 'activity_score' limit?: integer offset?: integer user_modified_filters?: Record @@ -479,6 +480,7 @@ export interface EntityNode extends Node { custom_name?: string math?: MathType math_property?: string + math_property_type?: string math_hogql?: string math_group_type_index?: 0 | 1 | 2 | 3 | 4 /** Properties configurable in the interface */ @@ -834,6 +836,8 @@ export type TrendsFilter = { display?: TrendsFilterLegacy['display'] /** @default false */ showLegend?: TrendsFilterLegacy['show_legend'] + /** @default false */ + showAlertThresholdLines?: boolean breakdown_histogram_bin_count?: TrendsFilterLegacy['breakdown_histogram_bin_count'] // TODO: fully move into BreakdownFilter /** @default numeric */ aggregationAxisFormat?: TrendsFilterLegacy['aggregation_axis_format'] diff --git a/frontend/src/queries/utils.ts b/frontend/src/queries/utils.ts index ed9cfc8d2fcf1..f2828675a643d 100644 --- a/frontend/src/queries/utils.ts +++ b/frontend/src/queries/utils.ts @@ -301,6 +301,13 @@ export const getShowLegend = (query: InsightQueryNode): boolean | undefined => { return undefined } +export const getShowAlertThresholdLines = (query: InsightQueryNode): boolean | undefined => { + if (isTrendsQuery(query)) { + return query.trendsFilter?.showAlertThresholdLines + } + return undefined +} + export const getShowLabelsOnSeries = (query: InsightQueryNode): boolean | undefined => { if (isTrendsQuery(query)) { return query.trendsFilter?.showLabelsOnSeries diff --git a/frontend/src/scenes/error-tracking/errorTrackingLogic.ts b/frontend/src/scenes/error-tracking/errorTrackingLogic.ts index 6db23821973ba..c1a847a8ab647 100644 --- a/frontend/src/scenes/error-tracking/errorTrackingLogic.ts +++ b/frontend/src/scenes/error-tracking/errorTrackingLogic.ts @@ -1,11 +1,10 @@ import type { LemonSegmentedButtonOption } from '@posthog/lemon-ui' import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea' -import { UniversalFiltersGroup } from 'lib/components/UniversalFilters/UniversalFilters' import { FEATURE_FLAGS } from 'lib/constants' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' import { DateRange } from '~/queries/schema' -import { FilterLogicalOperator } from '~/types' +import { FilterLogicalOperator, UniversalFiltersGroup } from '~/types' import type { errorTrackingLogicType } from './errorTrackingLogicType' diff --git a/frontend/src/scenes/error-tracking/queries.ts b/frontend/src/scenes/error-tracking/queries.ts index 0358e9096fd6d..6061773ecc562 100644 --- a/frontend/src/scenes/error-tracking/queries.ts +++ b/frontend/src/scenes/error-tracking/queries.ts @@ -1,4 +1,3 @@ -import { UniversalFiltersGroup } from 'lib/components/UniversalFilters/UniversalFilters' import { dayjs } from 'lib/dayjs' import { range } from 'lib/utils' @@ -11,7 +10,7 @@ import { InsightVizNode, NodeKind, } from '~/queries/schema' -import { AnyPropertyFilter, BaseMathType, ChartDisplayType, PropertyGroupFilter } from '~/types' +import { AnyPropertyFilter, BaseMathType, ChartDisplayType, PropertyGroupFilter, UniversalFiltersGroup } from '~/types' export type SparklineConfig = { value: number diff --git a/frontend/src/scenes/experiments/MetricSelector.tsx b/frontend/src/scenes/experiments/MetricSelector.tsx index 975ea070d11a4..4f4e7b6e1e262 100644 --- a/frontend/src/scenes/experiments/MetricSelector.tsx +++ b/frontend/src/scenes/experiments/MetricSelector.tsx @@ -132,6 +132,7 @@ export function ExperimentInsightCreator({ insightProps }: { insightProps: Insig seriesIndicatorType={isTrends ? undefined : 'numeric'} sortable={isTrends ? undefined : true} showNestedArrow={isTrends ? undefined : true} + showNumericalPropsOnly={isTrends} propertiesTaxonomicGroupTypes={[ TaxonomicFilterGroupType.EventProperties, TaxonomicFilterGroupType.PersonProperties, @@ -139,6 +140,8 @@ export function ExperimentInsightCreator({ insightProps }: { insightProps: Insig TaxonomicFilterGroupType.Cohorts, TaxonomicFilterGroupType.Elements, TaxonomicFilterGroupType.HogQLExpression, + TaxonomicFilterGroupType.DataWarehouseProperties, + TaxonomicFilterGroupType.DataWarehousePersonProperties, ]} />
diff --git a/frontend/src/scenes/heatmaps/HeatmapsBrowser.tsx b/frontend/src/scenes/heatmaps/HeatmapsBrowser.tsx index 99c561140e765..c73214f15571e 100644 --- a/frontend/src/scenes/heatmaps/HeatmapsBrowser.tsx +++ b/frontend/src/scenes/heatmaps/HeatmapsBrowser.tsx @@ -1,5 +1,6 @@ import { IconCollapse, IconGear } from '@posthog/icons' import { LemonBanner, LemonButton, LemonInputSelect, LemonSkeleton, Spinner, Tooltip } from '@posthog/lemon-ui' +import clsx from 'clsx' import { BindLogic, useActions, useValues } from 'kea' import { AuthorizedUrlList } from 'lib/components/AuthorizedUrlList/AuthorizedUrlList' import { appEditorUrl, AuthorizedUrlListType } from 'lib/components/AuthorizedUrlList/authorizedUrlListLogic' @@ -152,7 +153,7 @@ function FilterPanel(): JSX.Element { } = useActions(logic) return ( -
+
{filterPanelCollapsed ? (
- ) : null + ) } function EmbeddedHeatmapBrowser({ @@ -226,7 +225,7 @@ function EmbeddedHeatmapBrowser({ }): JSX.Element | null { const logic = heatmapsBrowserLogic() - const { browserUrl } = useValues(logic) + const { browserUrl, loading, iframeBanner } = useValues(logic) const { onIframeLoad, setIframeWidth } = useActions(logic) const { width: iframeWidth } = useResizeObserver({ ref: iframeRef }) @@ -238,8 +237,8 @@ function EmbeddedHeatmapBrowser({
- - + {loading ? : null} + {!loading && iframeBanner ? : null}