-
Notifications
You must be signed in to change notification settings - Fork 2.2k
feat: new platform addons #32624
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: new platform addons #32624
Conversation
| lemonToast.success('Your trial has been activated!') | ||
| } catch (e) { | ||
| lemonToast.error('There was an error activating your trial. Please try again or contact support.') | ||
| } finally { | ||
| await breakpoint(400) | ||
| window.location.reload() | ||
| } catch (e) { | ||
| lemonToast.error('There was an error activating your trial. Please try again or contact support.') | ||
| actions.setTrialLoading(false) | ||
| actions.setTrialModalOpen(false) | ||
| actions.loadBilling() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The window.location.reload() call is placed before the error handling block, which means if an error occurs during trial activation, the page will reload before the error handling code executes. This prevents the error toast from displaying and proper cleanup from occurring.
Consider restructuring this to ensure error handling works correctly:
try {
await api.create('api/billing/trials/activate', {
target: props.product.type,
})
lemonToast.success('Your trial has been activated!')
await breakpoint(400)
window.location.reload() // Only reload on success
} catch (e) {
lemonToast.error('There was an error activating your trial. Please try again or contact support.')
actions.setTrialLoading(false)
actions.loadBilling()
}This ensures the page only reloads on successful trial activation, while error cases are properly handled.
| lemonToast.success('Your trial has been activated!') | |
| } catch (e) { | |
| lemonToast.error('There was an error activating your trial. Please try again or contact support.') | |
| } finally { | |
| await breakpoint(400) | |
| window.location.reload() | |
| } catch (e) { | |
| lemonToast.error('There was an error activating your trial. Please try again or contact support.') | |
| actions.setTrialLoading(false) | |
| actions.setTrialModalOpen(false) | |
| actions.loadBilling() | |
| try { | |
| await api.create('api/billing/trials/activate', { | |
| target: props.product.type, | |
| }) | |
| lemonToast.success('Your trial has been activated!') | |
| await breakpoint(400) | |
| window.location.reload() | |
| } catch (e) { | |
| lemonToast.error('There was an error activating your trial. Please try again or contact support.') | |
| actions.setTrialLoading(false) | |
| actions.loadBilling() | |
| } |
Spotted by Diamond
Is this helpful? React 👍 or 👎 to let us know.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR introduces platform addons and billing plan updates across PostHog's frontend, focusing on standardizing plan naming and supporting legacy products.
- Renamed plans from 'Totally free' to 'Free' and 'Ridiculously cheap' to 'Pay-as-you-go' across all billing components and fixtures
- Added new platform addons including Teams with features like priority support, unlimited projects, and SSO enforcement
- Added
legacy_productflag in types and special handling for legacy team add-ons with warning banners - Added
isSubscribedToAnotherAddoncheck to prevent multiple addon subscriptions - Introduced new Boost and Scale billing plans while maintaining legacy Teams plan support
14 file(s) reviewed, 3 comment(s)
Edit PR Review Bot Settings | Greptile
| product.addons.find((addon) => addon.legacy_product && addon.subscribed) | ||
| ?.name | ||
| }{' '} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Multiple find() calls on the same condition. Consider storing the result in a variable
| } | ||
| return true | ||
| }) | ||
| .map((addon, i) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Using array index as React key may cause issues with list rendering if items are reordered
| const parentProduct = billing.products.find((product: any) => | ||
| product.addons.find((a: BillingProductV2AddonType) => a.type === addon.type) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: Type any used for product. Should explicitly type this as BillingProductV2Type to ensure type safety
| const parentProduct = billing.products.find((product: any) => | |
| product.addons.find((a: BillingProductV2AddonType) => a.type === addon.type) | |
| ) | |
| const parentProduct = billing.products.find((product: BillingProductV2Type) => | |
| product.addons?.find((a: BillingProductV2AddonType) => a.type === addon.type) | |
| ) |
📸 UI snapshots have been updated4 snapshot changes in total. 0 added, 4 modified, 0 deleted:
Triggered by this commit. |
|
Size Change: 0 B Total Size: 3.72 MB ℹ️ View Unchanged
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Very happy to see "Pay-as-you-go" instead of "Ridiculously cheap".
Left a couple of minor suggestions and have one overall feedback on the billing page UI/UX. Neither is a blocker for merging though.
Currently, it's not clear from the UI that the platform add-ons are incremental and all the features from the previous ones are included. We could e.g.
- State this more clearly on each card ("Everything included in Scale plus: ...")
- If you are subscribed to a platform add-on, make the other add-on cards a bit less opaque, lighter grey, kind of like a disabled button
- Indicate progression between different add-on tiers visually
- Right now they have the same icon - maybe use a different one and show it in different quantity depending on the add-on plan? Maybe like army pins - 1/2/3/4 star pin/badge
- Instead of full width boxes, use a 3-4 columns view (probably tight, esp. with 4), like on pricing pages
- Probably too weird (and probably fits better for posthog.com pricing pages, if at all) but maybe we could get a hedgehog version of the wrestling guy reaction meme
Might be a good follow up task for @yasen-posthog to experiment with!
| getDescription: (billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => ( | ||
| getDescription: (_billingPlan: BillingPlan, scrollToProduct: (productType: string) => void) => ( | ||
| <p> | ||
| If you're growing like crazy, you might want to check out the{' '} | ||
| {billingPlan !== BillingPlan.Teams ? ( | ||
| <> | ||
| {scrollToProduct ? ( | ||
| <> | ||
| <Link onClick={() => scrollToProduct('teams')}>Teams</Link> | ||
| {' or '} | ||
| </> | ||
| ) : ( | ||
| 'Teams or ' | ||
| )} | ||
| </> | ||
| ) : null} | ||
| {scrollToProduct ? <Link onClick={() => scrollToProduct('enterprise')}>Enterprise</Link> : 'Enterprise'}{' '} | ||
| plan. | ||
| If you're growing like crazy, you might want to check out our{' '} | ||
| {scrollToProduct ? ( | ||
| <Link onClick={() => scrollToProduct('platform_and_support')}>Platform add-ons</Link> | ||
| ) : ( | ||
| 'Platform add-ons' | ||
| )} | ||
| . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we still check if they already have the platform add-on to avoid showing it,at least if they're already on Enterprise? Small edge case though.
| .filter((addon) => { | ||
| if ( | ||
| product.type === 'platform_and_support' && | ||
| addon.legacy_product && | ||
| !addon.subscribed | ||
| ) { | ||
| return false | ||
| } | ||
| return true | ||
| }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: move the if to the existing filter to avoid looping twice
I'm also a fan of having things like this in the logic, via something like e.g. visibleAddons selector - makes reading these components easier
| [BillingPlan.Boost]: planTeams, // TODO: Add Boost badge | ||
| [BillingPlan.Scale]: planTeams, // TODO: Add Scale badge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to request this art work asap as it might take a while

Important
👉 Stay up-to-date with PostHog coding conventions for a smoother review.
Changes
Adding new platform addons. Main changes
This PR should be shipped first. Then billing then posthog.com
See related PRs
Did you write or update any docs for this change?
How did you test this code?