From 813ea5b82621c7f93056c61cb07389b78d3da62e Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Tue, 29 Oct 2024 17:15:22 -0400 Subject: [PATCH] enough hogs logic --- .../scenes/billing/UnsubscribeSurveyModal.tsx | 346 +++++++++++------- frontend/src/scenes/billing/billingLogic.tsx | 1 - .../src/scenes/billing/billingProductLogic.ts | 30 +- 3 files changed, 241 insertions(+), 136 deletions(-) diff --git a/frontend/src/scenes/billing/UnsubscribeSurveyModal.tsx b/frontend/src/scenes/billing/UnsubscribeSurveyModal.tsx index 123f46c02739a..20ba7cc4b8eb6 100644 --- a/frontend/src/scenes/billing/UnsubscribeSurveyModal.tsx +++ b/frontend/src/scenes/billing/UnsubscribeSurveyModal.tsx @@ -1,7 +1,18 @@ import './UnsubscribeSurveyModal.scss' -import { LemonBanner, LemonButton, LemonCheckbox, LemonLabel, LemonModal, LemonTextArea, Link } from '@posthog/lemon-ui' +import { + LemonBanner, + LemonButton, + LemonCheckbox, + LemonDivider, + LemonLabel, + LemonModal, + LemonTextArea, + Link, +} from '@posthog/lemon-ui' import { useActions, useValues } from 'kea' +import { HeartHog } from 'lib/components/hedgehogs' +import { useHogfetti } from 'lib/components/Hogfetti/Hogfetti' import { supportLogic } from 'lib/components/Support/supportLogic' import { BillingProductV2AddonType, BillingProductV2Type } from '~/types' @@ -26,10 +37,20 @@ export const UnsubscribeSurveyModal = ({ }: { product: BillingProductV2Type | BillingProductV2AddonType }): JSX.Element | null => { - const { surveyID, surveyResponse, isAddonProduct } = useValues(billingProductLogic({ product })) - const { setSurveyResponse, toggleSurveyReason, reportSurveyDismissed } = useActions( - billingProductLogic({ product }) + const { trigger, HogfettiComponent } = useHogfetti() + + const { surveyID, surveyResponse, isAddonProduct, surveyStep } = useValues( + billingProductLogic({ product, hogfettiTrigger: trigger }) ) + const { + setSurveyResponse, + toggleSurveyReason, + reportSurveyDismissed, + setStepTwo, + resetStep, + setHedgehogSatisfied, + triggerMoreHedgehogs, + } = useActions(billingProductLogic({ product })) const { deactivateProduct, resetUnsubscribeError } = useActions(billingLogic) const { unsubscribeError, billingLoading, billing } = useValues(billingLogic) const { unsubscribeDisabledReason, itemsToDisable } = useValues(exportsUnsubscribeTableLogic) @@ -50,146 +71,205 @@ export const UnsubscribeSurveyModal = ({ actionVerb = isAddonProduct ? 'removing this addon' : 'downgrading' } - return ( - { - reportSurveyDismissed(surveyID) - resetUnsubscribeError() - }} - width="max(44vw)" - title={isAddonProduct ? action : `${action} from ${product.name}`} - footer={ - <> - { - reportSurveyDismissed(surveyID) - }} - > - Cancel - - { - deactivateProduct( - billing?.subscription_level === 'paid' && !isAddonProduct - ? 'all_products' - : product.type - ) - }} - loading={billingLoading} - > - {action} - - - } - > -
- {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')) { + setStepTwo() + 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 renderStep2 = (): 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} + + { + resetStep() + setSurveyResponse( + '$survey_response_2', + surveyResponse['$survey_response_2'].filter((r) => r !== 'Not enough hedgehogs') + ) }} - /> + > + 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() + resetStep() + }} + width="max(44vw)" + title={isAddonProduct ? action : `${action} from ${product.name}`} + footer={ + surveyStep === 1 ? ( + <> + { + reportSurveyDismissed(surveyID) + }} + > + Cancel + + + {action} + + + ) : null + } + > + {surveyStep === 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} +
+ ) : ( + renderStep2() + )} + + ) } diff --git a/frontend/src/scenes/billing/billingLogic.tsx b/frontend/src/scenes/billing/billingLogic.tsx index 8eae5896563c1..8aafd3c6d5eab 100644 --- a/frontend/src/scenes/billing/billingLogic.tsx +++ b/frontend/src/scenes/billing/billingLogic.tsx @@ -240,7 +240,6 @@ 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') actions.reportProductUnsubscribed(key) return parseBillingResponse(jsonRes) } catch (error: any) { diff --git a/frontend/src/scenes/billing/billingProductLogic.ts b/frontend/src/scenes/billing/billingProductLogic.ts index deaae97fbc689..5b4e65ee71844 100644 --- a/frontend/src/scenes/billing/billingProductLogic.ts +++ b/frontend/src/scenes/billing/billingProductLogic.ts @@ -1,4 +1,4 @@ -import { LemonDialog } from '@posthog/lemon-ui' +import { LemonDialog, lemonToast } from '@posthog/lemon-ui' import { actions, connect, events, kea, key, listeners, path, props, reducers, selectors } from 'kea' import { forms } from 'kea-forms' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' @@ -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, }), + setStepTwo: true, + resetStep: true, + setHedgehogSatisfied: (satisfied: boolean) => ({ satisfied }), + triggerMoreHedgehogs: true, }), reducers({ billingLimitInput: [ @@ -151,6 +156,19 @@ export const billingProductLogic = kea([ toggleIsPlanComparisonModalOpen: (_, { highlightedFeatureKey }) => highlightedFeatureKey || null, }, ], + surveyStep: [ + 1 as number, + { + setStepTwo: () => 2, + resetStep: () => 1, + }, + ], + hedgehogSatisfied: [ + false as boolean, + { + setHedgehogSatisfied: (_, { satisfied }) => satisfied, + }, + ], }), selectors(({ values }) => ({ customLimitUsd: [ @@ -304,9 +322,11 @@ export const billingProductLogic = kea([ deactivateProductSuccess: async (_, breakpoint) => { if (!values.unsubscribeError && values.surveyID) { actions.reportSurveySent(values.surveyID, values.surveyResponse) + if (values.hedgehogSatisfied) { + lemonToast.success("We're sad to see you go, but glad you got enough hedgehogs!") + } } await breakpoint(200) - location.reload() }, setScrollToProductKey: ({ scrollToProductKey }) => { if (scrollToProductKey && scrollToProductKey === props.product.type) { @@ -330,6 +350,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: {