Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [1/n] Teams Downgrade Flow Experiment #27617

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/lib/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ export const FEATURE_FLAGS = {
REPLAY_LANDING_PAGE: 'replay-landing-page', // owner :#team-replay
CORE_WEB_VITALS: 'core-web-vitals', // owner: @rafaeelaudibert #team-web-analytics
LLM_OBSERVABILITY: 'llm-observability', // owner: #team-ai-product-manager
TEAMS_DOWNGRADE_FLOW: 'teams-downgrade-flow', // owner: @sjhavar #team-growth
} as const
export type FeatureFlagKey = (typeof FEATURE_FLAGS)[keyof typeof FEATURE_FLAGS]

Expand Down
39 changes: 26 additions & 13 deletions frontend/src/scenes/billing/BillingProductAddonActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import { getProration } from './billing-utils'
import { billingLogic } from './billingLogic'
import { formatFlatRate } from './BillingProductAddon'
import { billingProductLogic } from './billingProductLogic'
import { downgradeLogic } from './downgradeLogic'
import { DowngradeModal } from './DowngradeModal'

interface BillingProductAddonActionsProps {
productRef: React.RefObject<HTMLDivElement>
Expand All @@ -36,6 +38,8 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
cancelTrial,
} = useActions(billingProductLogic({ product: addon }))
const { featureFlags } = useValues(featureFlagLogic)
const { showDowngradeModal } = useActions(downgradeLogic)
const { isUserInExperiment } = useValues(downgradeLogic)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: make this more specific e.g. isUserInDowngradeFlowExperiment


const upgradePlan = currentAndUpgradePlans?.upgradePlan
const { prorationAmount, isProrated } = useMemo(
Expand Down Expand Up @@ -69,19 +73,28 @@ export const BillingProductAddonActions = ({ addon, productRef }: BillingProduct
return null
}
return (
<More
overlay={
<LemonButton
fullWidth
onClick={() => {
setSurveyResponse('$survey_response_1', addon.type)
reportSurveyShown(UNSUBSCRIBE_SURVEY_ID, addon.type)
}}
>
Remove add-on
</LemonButton>
}
/>
<>
<More
overlay={
<LemonButton
fullWidth
onClick={() => {
if (isUserInExperiment) {
showDowngradeModal({
addon,
})
} else {
setSurveyResponse('$survey_response_1', addon.type)
reportSurveyShown(UNSUBSCRIBE_SURVEY_ID, addon.type)
}
}}
>
Remove add-on
</LemonButton>
}
/>
<DowngradeModal />
</>
)
}

Expand Down
34 changes: 34 additions & 0 deletions frontend/src/scenes/billing/DowngradeModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { LemonButton, LemonModal } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'

import { downgradeLogic } from './downgradeLogic'

export function DowngradeModal(): JSX.Element | null {
const { isDowngradeModalOpen, currentAddon } = useValues(downgradeLogic)
const { hideDowngradeModal, handleDowngrade } = useActions(downgradeLogic)

if (!currentAddon) {
return null
}

return (
<LemonModal
isOpen={isDowngradeModalOpen}
onClose={hideDowngradeModal}
title={`Unsubscribe from ${currentAddon.name}`}
description="Your team is actively using these features and will lose access to it immediately"
footer={
<>
<LemonButton type="secondary" onClick={hideDowngradeModal}>
Cancel
</LemonButton>
<LemonButton type="primary" status="danger" onClick={handleDowngrade}>
Unsubscribe
</LemonButton>
</>
}
>
<div className="mt-2 mb-6">TODO: Add features that are used here</div>
</LemonModal>
)
}
63 changes: 63 additions & 0 deletions frontend/src/scenes/billing/downgradeLogic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { actions, connect, kea, listeners, path, reducers, selectors } from 'kea'
import { FEATURE_FLAGS, UNSUBSCRIBE_SURVEY_ID } from 'lib/constants'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'

import { BillingProductV2AddonType } from '~/types'

import { billingProductLogic } from './billingProductLogic'
import type { downgradeLogicType } from './downgradeLogicType'

export interface ShowDowngradeModalPayload {
addon: BillingProductV2AddonType
}

export const downgradeLogic = kea<downgradeLogicType>([
path(['scenes', 'billing', 'downgradeLogic']),

connect({
values: [featureFlagLogic, ['featureFlags']],
}),

actions({
showDowngradeModal: (payload: ShowDowngradeModalPayload) => ({ payload }),
hideDowngradeModal: true,
handleDowngrade: true,
}),

reducers({
isDowngradeModalOpen: [
false,
{
showDowngradeModal: () => true,
hideDowngradeModal: () => false,
},
],
currentAddon: [
null as BillingProductV2AddonType | null,
{
showDowngradeModal: (_, { payload }) => payload.addon,
hideDowngradeModal: () => null,
},
],
}),

selectors({
isUserInExperiment: [
(s) => [s.featureFlags],
(featureFlags): boolean => {
return featureFlags[FEATURE_FLAGS.TEAMS_DOWNGRADE_FLOW] === 'test'
},
],
}),

listeners(({ actions, values }) => ({
handleDowngrade: async () => {
if (values.currentAddon) {
const logic = billingProductLogic({ product: values.currentAddon })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some random kea related questions:

How does this work with pulling in a logic directly, do we need to mount it?

Is this a common kea pattern, or is it more common to use connect? Not sure if using connect is possible here given you need to pass a product?

logic.actions.setSurveyResponse('$survey_response_1', values.currentAddon.type)
logic.actions.reportSurveyShown(UNSUBSCRIBE_SURVEY_ID, values.currentAddon.type)
}
actions.hideDowngradeModal()
},
})),
])
Loading