-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New pricing plan #1935
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
New pricing plan #1935
Conversation
new plan, small fixes
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThe PR introduces a new "Data Rooms Premium" tier across billing: plan constants, limits, Stripe plan entries, webhook handling, usePlan surfaces, UI for selecting/upgrading plans, sidebar/team flows, and settings upgrade pages. Changes
Possibly related PRs
Pre-merge checks❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 2
🧹 Nitpick comments (8)
ee/limits/constants.ts (1)
82-96: Data Rooms Premium limits look consistent; consider deduplicating with PlusThe premium limits are coherent (effectively “Plus with more seats”). To reduce drift risk between Plus and Premium, you could
...DATAROOMS_PLUS_PLAN_LIMITSand override only the fields that differ, but current explicit object is also fine.components/sidebar/team-switcher.tsx (1)
48-49: Premium-gated team creation logic is sound; dropdown item can be DRY-ed upConditioning “Add new team” on
isDataroomsPremiumand otherwise routing throughUpgradePlanModalforDataRoomsPremiumaligns well with the new tier model. The two branches share identicalDropdownMenuItemmarkup though; you could extract that into a smallAddTeamMenuItemJSX fragment and wrap it with eitherAddTeamModalorUpgradePlanModalto keep this easier to maintain.Also applies to: 108-137
components/sidebar/app-sidebar.tsx (1)
55-64: Premium badge integration is correct; consider a more explicit labelHiding the generic plan badge when
isDataroomsPremiumand showing a dedicated “Premium” pill keeps the header clean and unambiguous. If you expect more “premium-ish” variants later, you might consider “Data Rooms Premium” as the badge text for extra clarity, but the current implementation is functionally fine.Also applies to: 219-233
pages/settings/upgrade.tsx (2)
90-103: PlanTypeSelector only toggles 2 views while state supports 3
planTypeandgetInitialViewsupport"documents" | "datarooms" | "business-datarooms", and the effect correctly syncs fromrouter.query.view. However,PlanTypeSelectorrenders only two buttons (Document Sharing / Data Rooms), and theshowBusinessDataroomsprop is currently unused. This means the “Business + Data Rooms” view is reachable via?view=business-dataroomsbut not discoverable from the on-page toggle.If that’s intentional (i.e., only certain entrypoints should show the combined view) then it’s fine, but you may want to either:
- Remove
showBusinessDataroomsand keep the selector explicitly 2-state, or- Use
showBusinessDataroomsto render a third button and widen theonChangesignature accordingly so all three views can be navigated in-page.Also applies to: 139-157, 185-190
158-164: Card layout for the three plan groups is duplicated and has some unreachable branchesThe three sections (“Document Sharing”, “Business + Data Rooms”, “Data Rooms”) all implement nearly identical card markup: border color selection, badge rendering, pricing lookup via
PLANS.find((p) => p.name === planOption), feature list, and upgrade CTA. That works, but it’s a lot of duplicated JSX and fetch/Stripe wiring to keep in sync across tiers.A few small observations:
- In the Document Sharing grid,
documentSharingPlansnever includesPlanEnum.DataRoomsPlusorPlanEnum.DataRoomsPremium, yet styling branches check for those values in the border color and badge logic, so those branches are currently dead code in this section.- In the other two grids, the
planOption === PlanEnum.Businessbranch in the card border/badge logic is only relevant where Business is actually in the mapped list (the datarooms-only grid never includes Business).Consider extracting a reusable
PlanCard(and possiblyPlanGrid) component that takesplanOption,period,teamPlan, etc., and centralizes:
- The border/badge selection for Business / DataRoomsPlus / DataRoomsPremium.
- The repeated manage/upgrade fetch + Stripe checkout logic.
That would remove the unreachable branches in the first grid and make any future pricing/feature changes less error‑prone.
Also applies to: 192-341, 343-499, 501-655
components/billing/upgrade-plan-modal.tsx (3)
99-141: PlanSelector three-way toggle is clear; consider reusing the tier typeThe three-option segmented control with
"base" | "plus" | "premium"is straightforward and keeps the API clean. To avoid repeating the literal union in multiple places (here and indataRoomsPlanSelection), consider extracting a small alias, e.g.type DataRoomsTier = "base" | "plus" | "premium";, and reusing it forvalue,onChange, and the state hook.
168-181: DataRooms selection state and plansToShow mapping look correct overallInitializing
dataRoomsPlanSelectionto"base"and resetting it in theopeneffect when the dialog closes prevents stale selections leaking between sessions, which is good. The extendedplansToShowswitch covers the new DataRoomsPlus → DataRoomsPremium path while preserving existing Pro/Business flows. If you care about micro-optimizations/readability,const isDataRoomsUpgrade = plansToShow.includes(PlanEnum.DataRooms);could be hoisted outside themapsince it’s constant per render.Also applies to: 195-196
238-255: effectivePlan/displayPlanName wiring is consistent; strengthen keys and premium exposureMapping
dataRoomsPlanSelectionintoeffectivePlananddisplayPlanNamekeepsgetPlanFeatures, the PLANS price lookup,getPriceIdFromPlan, headings, and button copy all in sync, which is the right approach.A couple of non-blocking points:
- Using
key={displayPlanName}works today but is a bit brittle: if future changes ever cause two cards to share the samedisplayPlanName, you’ll hit React key collisions. A composite key likekey={`${planOption}-${displayPlanName}`}would guard against that while still forcing a remount when the user switches tiers.- From a UX perspective, when
clickedPlan === PlanEnum.DataRoomsthe modal only ever shows DataRooms and DataRoomsPlus (no premium card or selector). The premium tier is reachable whenclickedPlanis DataRoomsPlus/DataRoomsPremium (two-card layout) or via the Business → Data Rooms selector, but users entering via a “Data Rooms” CTA won’t see premium directly. If the intent is to always surface the new premium tier from any Data Rooms entrypoint, you may want to includePlanEnum.DataRoomsPremiumin thatplansToShowcase or enable the selector there as well.- Ensure
PLANSandgetPriceIdFromPlanboth have entries keyed exactly byPlanEnum.DataRoomsPremium; otherwise price display or checkout for the premium tier will fail even though the UI allows selecting it.Also applies to: 260-287, 335-340, 395-397
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
components/billing/add-seat-modal.tsx(2 hunks)components/billing/upgrade-plan-container.tsx(2 hunks)components/billing/upgrade-plan-modal.tsx(9 hunks)components/sidebar/app-sidebar.tsx(3 hunks)components/sidebar/team-switcher.tsx(3 hunks)components/teams/add-team-member-modal.tsx(3 hunks)ee/limits/constants.ts(1 hunks)ee/limits/server.ts(2 hunks)ee/stripe/constants.ts(7 hunks)ee/stripe/utils.ts(1 hunks)ee/stripe/webhooks/checkout-session-completed.ts(3 hunks)ee/stripe/webhooks/customer-subscription-updated.ts(3 hunks)lib/swr/use-billing.ts(2 hunks)pages/settings/upgrade.tsx(7 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
ee/stripe/webhooks/customer-subscription-updated.ts (1)
ee/limits/constants.ts (3)
DATAROOMS_PLUS_PLAN_LIMITS(66-80)DATAROOMS_PREMIUM_PLAN_LIMITS(82-96)PRO_PLAN_LIMITS(27-36)
pages/settings/upgrade.tsx (4)
ee/stripe/constants.ts (1)
getPlanFeatures(216-283)ee/stripe/utils.ts (1)
PLANS(105-285)ee/stripe/client.ts (1)
getStripe(6-30)lib/utils.ts (1)
capitalize(160-163)
ee/limits/server.ts (1)
ee/limits/constants.ts (1)
DATAROOMS_PREMIUM_PLAN_LIMITS(82-96)
components/billing/upgrade-plan-modal.tsx (2)
lib/utils.ts (1)
cn(18-20)ee/stripe/constants.ts (1)
getPlanFeatures(216-283)
components/sidebar/team-switcher.tsx (3)
lib/swr/use-billing.ts (1)
usePlan(87-138)components/teams/add-team-modal.tsx (1)
AddTeamModal(29-112)components/billing/upgrade-plan-modal.tsx (1)
UpgradePlanModal(146-432)
ee/stripe/webhooks/checkout-session-completed.ts (1)
ee/limits/constants.ts (3)
DATAROOMS_PLUS_PLAN_LIMITS(66-80)DATAROOMS_PREMIUM_PLAN_LIMITS(82-96)PRO_PLAN_LIMITS(27-36)
🔇 Additional comments (12)
ee/stripe/constants.ts (5)
85-85: Clarify commented retention feature.The retention feature is commented out for the Pro plan. Please confirm whether this is intentional or if analytics retention should still be displayed for the Pro tier.
117-118: Verify commented features in Business plan.Several features are commented out in the Business plan (file-types-advanced on line 117). Please confirm these features should be hidden from the Business tier or if this is temporary.
144-147: Clarify commented audit log and retention in Data Rooms plan.Multiple features are commented out including audit log (line 144) and retention (line 147). Please verify these features are intentionally removed from the Data Rooms tier or if this represents incomplete work.
41-67: LGTM! Consistent pricing terminology.The update from "per user" to "per additional team member" across all plans provides clearer communication about the pricing model. The Premium tier pricing is properly integrated.
179-203: Premium plan features look comprehensive.The new Data Rooms Premium tier features are well-defined with appropriate highlights for unlimited storage and clear value propositions (teams, SSO, whitelabeling, API access, dedicated support).
components/billing/add-seat-modal.tsx (1)
188-194: Good UX enhancement with upgrade option.Adding the upgrade link provides users with a clear alternative to simply adding more seats. This helps guide users toward the most cost-effective solution for their needs.
ee/limits/server.ts (1)
9-9: LGTM! Premium plan limits properly integrated.The addition of
DATAROOMS_PREMIUM_PLAN_LIMITSto the plan limits map follows the established pattern and correctly extends the system to support the new premium tier.Also applies to: 28-28
components/billing/upgrade-plan-container.tsx (1)
48-48: LGTM! Premium plan display properly integrated.The conditional rendering correctly displays "Premium" for the Data Rooms Premium tier, maintaining consistency with the existing "Datarooms+" display logic. The integration with the
isDataroomsPremiumflag fromusePlanis correct.Also applies to: 295-300
ee/stripe/webhooks/checkout-session-completed.ts (1)
5-5: LGTM! Webhook handling for premium plan is complete.The webhook correctly handles checkout completion for Data Rooms Premium subscriptions by:
- Importing the premium limits constant
- Extending the type union to include premium limits
- Adding the conditional branch to clone premium limits when
plan.slug === "datarooms-premium"This follows the established pattern and will properly provision premium subscriptions.
Also applies to: 67-78
lib/swr/use-billing.ts (1)
45-46: LGTM! Plan type extensions are properly implemented.The changes correctly:
- Extend
BasePlanunion to include"datarooms-plus"and"datarooms-premium"- Update
isDataroomsto recognize all three dataroom tier variants- Add the
isDataroomsPremiumboolean flag for tier-specific logicThe implementation follows the established pattern and enables downstream components to properly handle the new premium tier.
Also applies to: 131-133
ee/stripe/webhooks/customer-subscription-updated.ts (1)
3-9: Correct mapping ofdatarooms-premiumto its limitsThe new import, union extension, and
plan.slug === "datarooms-premium"branch correctly wire the premium plan toDATAROOMS_PREMIUM_PLAN_LIMITSwhile preserving the existing quantity→users logic.Also applies to: 66-82
components/billing/upgrade-plan-modal.tsx (1)
405-415: Footer copy and “See all plans” link look good; confirm view query handlingUpdating the copy to “unlimited visitors” is consistent with the Data Rooms terminology. The conditional query string on the “See all plans” link (
?view=documentsfor Pro,?view=business-dataroomsfor Business, otherwise default) makes sense; just double-check that/settings/upgradeuses those exactviewvalues and that the fallback case (Data Rooms tiers) lands users on the intended tab.
|
|
||
| // Redirect to team members page | ||
| router.push("/settings/people"); |
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.
🧩 Analysis chain
Verify redirect behavior across all modal usage contexts.
The unconditional redirect to /settings/people may disrupt the user flow if this modal is invoked from pages other than the settings area. For example, if a user adds a team member from the team switcher or another context, they might expect to remain on their current page.
Run the following script to identify all locations where AddTeamMembers is used:
🏁 Script executed:
#!/bin/bash
# Find all usages of AddTeamMembers component
rg -n "AddTeamMembers" --type=tsx --type=ts -C3Length of output: 27
---
🏁 Script executed:
```shell
#!/bin/bash
# Find all usages of AddTeamMembers component without type specification
rg -n "AddTeamMembers" -C3
Length of output: 3445
🏁 Script executed:
#!/bin/bash
# Also search for imports of the modal to understand usage patterns
rg -n "add-team-member-modal" -C3Length of output: 1131
🏁 Script executed:
#!/bin/bash
# Find file extensions in the components/teams directory to understand structure
fd . components/teams --type fLength of output: 150
---
🏁 Script executed:
```shell
#!/bin/bash
# Find all usages of team-switcher component to understand where it appears
rg -n "team-switcher" -C2
Length of output: 390
🏁 Script executed:
#!/bin/bash
# Check the content of add-team-member-modal.tsx to see the full redirect context
cat components/teams/add-team-member-modal.tsx | head -100Length of output: 2667
Fix unconditional redirect that disrupts user flow when adding team members from sidebar.
The modal redirects to /settings/people unconditionally after a successful invitation, which disrupts users who invoke it from the sidebar (used throughout the app, not just settings). Users adding members while on other pages (dashboard, projects, etc.) will be unexpectedly redirected away.
Options:
- Remove the redirect entirely (modal closes, user stays on current page)
- Make redirect conditional (e.g., only redirect if already on settings/people)
- Accept a callback prop to let parent components control post-invitation navigation
🤖 Prompt for AI Agents
In components/teams/add-team-member-modal.tsx around lines 86-88, remove the
unconditional router.push("/settings/people") after a successful invitation and
instead either 1) simply close the modal so the user stays on the current page,
or 2) make the redirect conditional by checking the current route and only
calling router.push if the user is already on /settings/people, or 3) expose and
call an optional onSuccess callback prop (invoked with the invitation result) so
parent components can decide navigation; implement one of these approaches and
ensure the modal close logic still runs after a successful invite.
| { | ||
| name: "Data Rooms Premium", | ||
| slug: "datarooms-premium", | ||
| minQuantity: 10, | ||
| price: { | ||
| monthly: { | ||
| amount: 699, | ||
| unitPrice: 6990, | ||
| priceIds: { | ||
| test: { | ||
| old: "price_placeholder_test_old", | ||
| new: "price_1SUWeXBYvhH6u7U7u7CJgsRE", | ||
| }, | ||
| production: { | ||
| old: "price_placeholder_prod_old", | ||
| new: "price_1SUWXqBYvhH6u7U7SJKKOCKU", | ||
| }, | ||
| }, | ||
| }, | ||
| yearly: { | ||
| amount: 549, | ||
| unitPrice: 5490, | ||
| priceIds: { | ||
| test: { | ||
| old: "price_placeholder_test_yearly_old", | ||
| new: "price_1SUWhQBYvhH6u7U7BE6vVLcf", | ||
| }, | ||
| production: { | ||
| old: "price_placeholder_prod_yearly_old", | ||
| new: "price_1SUWWqBYvhH6u7U7I5MpZ43K", | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, |
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.
Verify placeholder price IDs for old accounts.
The Data Rooms Premium plan uses placeholder price IDs for old accounts (e.g., price_placeholder_test_old on line 260). If legacy customers attempt to upgrade to the Premium tier, these placeholders will cause getPlanFromPriceId to fail. Please confirm whether:
- Premium tier should be unavailable to old accounts (if so, add validation)
- Real price IDs need to be created for old accounts
Run the following script to verify if old account handling exists elsewhere:
#!/bin/bash
# Check for old account gating or validation in upgrade flows
rg -n "isOldAccount|isOld.*premium" --type=ts --type=tsx -C3There 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.
Actionable comments posted: 0
🧹 Nitpick comments (4)
components/billing/upgrade-plan-modal.tsx (4)
99-142: PlanSelector premium extension looks correct; consider de-duplicating the value typeThe segmented control correctly introduces a third “premium” option and wires
onChange("base" | "plus" | "premium")consistently. To avoid repeating the literal union in multiple places (PlanSelectorprops,dataRoomsPlanSelectionstate), consider extracting a local type alias, e.g.type DataRoomsTier = "base" | "plus" | "premium";, and reusing it for stronger coherence.
168-170: Data Rooms tier selection logic works for Business and Plus paths; confirm intended behavior forclickedPlan = DataRoomsThe
dataRoomsPlanSelectionstate +effectivePlan/displayPlanNamewiring correctly:
- Lets Business users see a single “Data Rooms” card whose price/features toggle between Base/Plus/Premium via
PlanSelector.- Lets DataRoomsPlus/DataRoomsPremium clicks show separate Plus vs Premium cards with correct pricing (
PLANS.find(...name === displayPlanName)) and price IDs (getPriceIdFromPlan({ planName: displayPlanName, ... })).However, when
clickedPlan === PlanEnum.DataRooms,plansToShowis[DataRooms, DataRoomsPlus], so:
DataRoomsPremiumis never surfaced.- The
PlanSelectoris not rendered (!plansToShow.includes(PlanEnum.DataRoomsPlus)is false), sodataRoomsPlanSelectionhas no effect in that flow.If users on the base Data Rooms plan are supposed to be able to upgrade directly to Premium from this modal, you may want to either:
- Include
PlanEnum.DataRoomsPremiuminplansToShowfor theDataRoomscase, or- Allow the
PlanSelectorfor theDataRoomscard even whenDataRoomsPlusis also present.Otherwise, this asymmetry (Business → Premium allowed, DataRooms → Premium not shown) is product‑driven but worth double‑checking.
Also applies to: 181-183, 240-259, 305-311
189-199: Effect dependencies may be incomplete for analytics + reset behaviorThe
useEffectusesanalytics,teamId, andsetDataRoomsPlanSelectionbut only declares[open, trigger]as dependencies. This can lead to staleteamId/analytics references if the team context changes without togglingopen.Consider adding
analyticsandteamId(and optionallysetDataRoomsPlanSelection) to the dependency array, or explicitly disabling the lint rule with a comment if you intend to lock the values to those at the time the modal is first opened.
405-415: Footer copy update is fine; consider aligningviewparam for Data Rooms tiersThe text change to “unlimited visitors” is straightforward and non‑breaking.
For the “See all plans” link, only
clickedPlan === PlanEnum.ProandclickedPlan === PlanEnum.Businesscurrently get a contextualview(documentsvsbusiness-datarooms). If the intent is that all Data Rooms‑related entry points (DataRooms, DataRoomsPlus, DataRoomsPremium) should land on the samebusiness-dataroomsview, consider extending this conditional to include those enums as well.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/billing/upgrade-plan-modal.tsx(8 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
components/billing/upgrade-plan-modal.tsx (2)
lib/utils.ts (1)
cn(18-20)ee/stripe/constants.ts (1)
getPlanFeatures(216-283)
🔇 Additional comments (1)
components/billing/upgrade-plan-modal.tsx (1)
275-276: UsingdisplayPlanNameandeffectivePlanfor features, pricing, and price IDs is consistentDeriving
effectivePlanforgetPlanFeaturesand usingdisplayPlanNamefor:
- the card title,
- pricing via
PLANS.find((p) => p.name === displayPlanName),- and
getPriceIdFromPlan({ planName: displayPlanName, ... })keeps the Data Rooms Base/Plus/Premium variants internally consistent. This ensures that when the user selects Plus or Premium, both the shown price/features and the Stripe priceId line up with the chosen tier.
Also applies to: 283-288, 291-303, 333-341, 345-348
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.