- {unsubscribeError && (
-
-
- {unsubscribeError.detail} {unsubscribeError.link}
-
-
- )}
- {isAddonProduct ? (
-
- We're sorry to see you go! Please note, you'll lose access to the addon features immediately.
-
- ) : (
-
- We're sorry to see you go! Please note, you'll lose access to platform features and usage limits
- will apply immediately. And if you have any outstanding invoices, they will be billed
- immediately.{' '}
-
- View invoices
-
-
- )}
+ const handleUnsubscribe = (): void => {
+ if (surveyResponse['$survey_response_2'].includes('Not enough hedgehogs')) {
+ setUnsubscribeModalStep(2)
+ triggerMoreHedgehogs()
+ } else {
+ deactivateProduct(billing?.subscription_level === 'paid' && !isAddonProduct ? 'all_products' : product.type)
+ }
+ }
-
- {billing?.subscription_level === 'paid'
- ? `Why are you ${actionVerb}?`
- : `Why are you ${actionVerb} from ${product.name}?`}{' '}
- (you can select multiple)
-
-
- {UNSUBSCRIBE_REASONS.map((reason) => (
-
toggleSurveyReason(reason)}
- className="w-full"
- labelClassName="w-full"
- />
- ))}
+ const renderHedgehogStep = (): JSX.Element => (
+
+
+
How about now? Was that enough hedgehogs?
+
Look at all these adorable hedgehogs dancing just for you! 🦔✨
+
+
-
-
{
- setSurveyResponse('$survey_response', value)
+
+
+
+ Still not enough! More hedgehogs! 🦔
+
+
+
+
+ {
+ setHedgehogSatisfied(true)
+ deactivateProduct(
+ billing?.subscription_level === 'paid' && !isAddonProduct ? 'all_products' : product.type
+ )
+ }}
+ >
+ Never enough, proceed with {action}
+
+ {
+ resetUnsubscribeModalStep()
+ reportSurveyDismissed(surveyID)
}}
- />
+ >
+ You convinced me to stay! 💕
+
+
+
+ )
-
-
- {'Are you looking to control your costs? Learn about ways to '}
- {
- reportSurveyDismissed(surveyID)
- }}
- >
- reduce your bill
-
- {`${product.type !== 'session_replay' ? ' or ' : ', '}`}
- {
- reportSurveyDismissed(surveyID)
- openSupportForm({ target_area: 'billing', isEmailFormOpen: true })
+ return (
+ <>
+
+ {
+ reportSurveyDismissed(surveyID)
+ resetUnsubscribeError()
+ resetUnsubscribeModalStep()
+ }}
+ width="max(44vw)"
+ title={
+ isAddonProduct
+ ? action
+ : product.type === 'platform_and_support'
+ ? `${action} your plan`
+ : `${action} from ${product.name}`
+ }
+ footer={
+ unsubscribeModalStep === 1 ? (
+ <>
+ {
+ reportSurveyDismissed(surveyID)
+ }}
+ >
+ Cancel
+
+
+ {action}
+
+ >
+ ) : null
+ }
+ >
+ {unsubscribeModalStep === 1 ? (
+
+ {unsubscribeError && (
+
+
+ {unsubscribeError.detail} {unsubscribeError.link}
+
+
+ )}
+ {isAddonProduct ? (
+
+ We're sorry to see you go! Please note, you'll lose access to the addon features
+ immediately.
+
+ ) : (
+
+ We're sorry to see you go! Please note, you'll lose access to platform features and
+ usage limits will apply immediately. And if you have any outstanding invoices, they will
+ be billed immediately.{' '}
+
+ View invoices
+
+
+ )}
+
+
+ {billing?.subscription_level === 'paid'
+ ? `Why are you ${actionVerb}?`
+ : `Why are you ${actionVerb} from ${product.name}?`}{' '}
+ (you can select multiple)
+
+
+ {UNSUBSCRIBE_REASONS.map((reason) => (
+ toggleSurveyReason(reason)}
+ className="w-full"
+ labelClassName="w-full"
+ />
+ ))}
+
+
+
{
+ setSurveyResponse('$survey_response', value)
}}
- >
- chat with support
-
- {product.type === 'session_replay' && (
- <>
- {', or '}
+ />
+
+
+
+ {'Are you looking to control your costs? Learn about ways to '}
{
reportSurveyDismissed(surveyID)
}}
>
- join our beta
+ reduce your bill
- {' for tuning recording volume with sampling and minimum duration.'}
- >
- )}
- .
-
-
-
- {includesPipelinesAddon && itemsToDisable.length > 0 ? (
-
-
Important: Disable remaining export apps
-
- To avoid unexpected impact on your data, you must explicitly disable the following apps and
- exports before unsubscribing:
-
-
-
- ) : null}
-
+ {`${product.type !== 'session_replay' ? ' or ' : ', '}`}
+ {
+ reportSurveyDismissed(surveyID)
+ openSupportForm({ target_area: 'billing', isEmailFormOpen: true })
+ }}
+ >
+ chat with support
+
+ {product.type === 'session_replay' && (
+ <>
+ {', or '}
+ {
+ reportSurveyDismissed(surveyID)
+ }}
+ >
+ join our beta
+
+ {' for tuning recording volume with sampling and minimum duration.'}
+ >
+ )}
+ .
+
+
+ {includesPipelinesAddon && itemsToDisable.length > 0 ? (
+
+
Important: Disable remaining export apps
+
+ To avoid unexpected impact on your data, you must explicitly disable the following
+ apps and exports before unsubscribing:
+
+
+
+ ) : null}
+
+ ) : (
+ renderHedgehogStep()
+ )}
+
+ >
)
}
diff --git a/frontend/src/scenes/billing/billingLogic.tsx b/frontend/src/scenes/billing/billingLogic.tsx
index 8eae5896563c13..eecb06c09059dd 100644
--- a/frontend/src/scenes/billing/billingLogic.tsx
+++ b/frontend/src/scenes/billing/billingLogic.tsx
@@ -231,6 +231,7 @@ export const billingLogic = kea
([
deactivateProduct: async (key: string) => {
// clear upgrade params from URL
+ // Note(@zach): This is not working properly. We need to look into this.
const currentURL = new URL(window.location.href)
currentURL.searchParams.delete('upgraded')
currentURL.searchParams.delete('products')
@@ -240,8 +241,12 @@ export const billingLogic = kea([
try {
const response = await api.getResponse('api/billing/deactivate?products=' + key)
const jsonRes = await getJSONOrNull(response)
- lemonToast.success('You have been unsubscribed')
+
+ lemonToast.success(
+ "You have been unsubscribed. We're sad to see you go. May the hedgehogs be ever in your favor."
+ )
actions.reportProductUnsubscribed(key)
+
return parseBillingResponse(jsonRes)
} catch (error: any) {
if (error.code) {
diff --git a/frontend/src/scenes/billing/billingProductLogic.ts b/frontend/src/scenes/billing/billingProductLogic.ts
index deaae97fbc689c..a2b3d9718fa46f 100644
--- a/frontend/src/scenes/billing/billingProductLogic.ts
+++ b/frontend/src/scenes/billing/billingProductLogic.ts
@@ -18,6 +18,7 @@ export interface BillingProductLogicProps {
product: BillingProductV2Type | BillingProductV2AddonType
productRef?: React.MutableRefObject
billingLimitInputRef?: React.MutableRefObject
+ hogfettiTrigger?: () => void
}
export const billingProductLogic = kea([
@@ -74,6 +75,10 @@ export const billingProductLogic = kea([
products,
redirectPath,
}),
+ setUnsubscribeModalStep: (step: number) => ({ step }),
+ resetUnsubscribeModalStep: true,
+ setHedgehogSatisfied: (satisfied: boolean) => ({ satisfied }),
+ triggerMoreHedgehogs: true,
}),
reducers({
billingLimitInput: [
@@ -151,6 +156,19 @@ export const billingProductLogic = kea([
toggleIsPlanComparisonModalOpen: (_, { highlightedFeatureKey }) => highlightedFeatureKey || null,
},
],
+ unsubscribeModalStep: [
+ 1 as number,
+ {
+ setUnsubscribeModalStep: (_, { step }) => step,
+ resetUnsubscribeModalStep: () => 1,
+ },
+ ],
+ hedgehogSatisfied: [
+ false as boolean,
+ {
+ setHedgehogSatisfied: (_, { satisfied }) => satisfied,
+ },
+ ],
}),
selectors(({ values }) => ({
customLimitUsd: [
@@ -304,12 +322,13 @@ export const billingProductLogic = kea([
deactivateProductSuccess: async (_, breakpoint) => {
if (!values.unsubscribeError && values.surveyID) {
actions.reportSurveySent(values.surveyID, values.surveyResponse)
+ await breakpoint(400)
+ document.getElementsByClassName('Navigation3000__scene')[0].scrollIntoView()
}
- await breakpoint(200)
- location.reload()
},
setScrollToProductKey: ({ scrollToProductKey }) => {
- if (scrollToProductKey && scrollToProductKey === props.product.type) {
+ // Only scroll to the product if it's an addon product. With subscribe to all products we don't need it for parent products.
+ if (scrollToProductKey && values.isAddonProduct && scrollToProductKey === props.product.type) {
setTimeout(() => {
if (props.productRef?.current) {
props.productRef?.current.scrollIntoView({
@@ -330,6 +349,12 @@ export const billingProductLogic = kea([
redirectPath && `&redirect_path=${redirectPath}`
}`
},
+ triggerMoreHedgehogs: async (_, breakpoint) => {
+ for (let i = 0; i < 5; i++) {
+ props.hogfettiTrigger?.()
+ await breakpoint(200)
+ }
+ },
})),
forms(({ actions, props, values }) => ({
billingLimitInput: {
diff --git a/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx b/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
index 2d8bba2f256bcc..95a525987b8a27 100644
--- a/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
+++ b/frontend/src/scenes/data-warehouse/new/sourceWizardLogic.tsx
@@ -6,6 +6,7 @@ import api from 'lib/api'
import posthog from 'posthog-js'
import { preflightLogic } from 'scenes/PreflightCheck/preflightLogic'
import { Scene } from 'scenes/sceneTypes'
+import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'
import {
@@ -16,6 +17,7 @@ import {
manualLinkSources,
ManualLinkSourceType,
PipelineTab,
+ ProductKey,
SourceConfig,
SourceFieldConfig,
} from '~/types'
@@ -731,6 +733,8 @@ export const sourceWizardLogic = kea([
['resetTable', 'createTableSuccess'],
dataWarehouseSettingsLogic,
['loadSources'],
+ teamLogic,
+ ['addProductIntent'],
],
}),
reducers({
@@ -1129,6 +1133,9 @@ export const sourceWizardLogic = kea([
setManualLinkingProvider: () => {
actions.onNext()
},
+ selectConnector: () => {
+ actions.addProductIntent({ product_type: ProductKey.DATA_WAREHOUSE, intent_context: 'selected connector' })
+ },
})),
urlToAction(({ actions }) => ({
'/data-warehouse/:kind/redirect': ({ kind = '' }, searchParams) => {
diff --git a/frontend/src/scenes/experiments/Experiment.stories.tsx b/frontend/src/scenes/experiments/Experiment.stories.tsx
index daab995ff3aa13..8d2aecd75ab1e8 100644
--- a/frontend/src/scenes/experiments/Experiment.stories.tsx
+++ b/frontend/src/scenes/experiments/Experiment.stories.tsx
@@ -116,6 +116,7 @@ const MOCK_FUNNEL_EXPERIMENT: Experiment = {
interval: 'day',
filter_test_accounts: true,
},
+ metrics: [],
archived: false,
created_by: {
id: 1,
@@ -172,6 +173,7 @@ const MOCK_TREND_EXPERIMENT: Experiment = {
},
},
},
+ metrics: [],
parameters: {
feature_flag_variants: [
{
@@ -277,6 +279,7 @@ const MOCK_TREND_EXPERIMENT_MANY_VARIANTS: Experiment = {
},
},
},
+ metrics: [],
parameters: {
feature_flag_variants: [
{
diff --git a/frontend/src/scenes/experiments/ExperimentView/Goal.tsx b/frontend/src/scenes/experiments/ExperimentView/Goal.tsx
index b0ef5701ec5c79..c68acb47f9df47 100644
--- a/frontend/src/scenes/experiments/ExperimentView/Goal.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/Goal.tsx
@@ -238,17 +238,16 @@ export function Goal(): JSX.Element {
Change goal
- {experimentInsightType === InsightType.TRENDS &&
- !experimentMathAggregationForTrends(experiment.filters) && (
- <>
-