Skip to content

feat: Add personal org warnings to checkout flow #3896

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

Merged
merged 13 commits into from
Jun 25, 2025

Conversation

spalmurray
Copy link
Contributor

@spalmurray spalmurray commented Jun 17, 2025

Adds a few changes that draw attention to the org you are about to change the plan of. Most notably, this adds a confirmation modal to the checkout flow and adds a banner letting you know you're about to change the plan of your personal org. This is because we've had several issues caused by users upgrading their personal org by accident. There is another issue that fixes a more core issue, but these are certainly improvements regardless.

Closes codecov/feedback#722
Closes codecov/feedback#651

Related to codecov/engineering-team#3587

Recording 2025-06-19 at 14 46 22
Recording 2025-06-19 at 14 51 20

@spalmurray spalmurray marked this pull request as ready for review June 17, 2025 16:45
@codecov-staging
Copy link

codecov-staging bot commented Jun 17, 2025

Codecov Report

Attention: Patch coverage is 91.54930% with 6 lines in your changes missing coverage. Please review.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...Routes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx 63.63% 4 Missing ⚠️
...PlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx 87.50% 2 Missing ⚠️
@@            Coverage Diff             @@
##             main    #3896      +/-   ##
==========================================
- Coverage   98.72%   98.66%   -0.06%     
==========================================
  Files         827      828       +1     
  Lines       15040    15071      +31     
  Branches     4306     4312       +6     
==========================================
+ Hits        14848    14870      +22     
- Misses        185      193       +8     
- Partials        7        8       +1     
Files with missing lines Coverage Δ
...roPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...ontrollers/ProPlanController/ProPlanController.tsx 100.00% <100.00%> (ø)
...trollers/ProPlanController/UserCount/UserCount.tsx 100.00% <100.00%> (ø)
...ryPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...lers/SentryPlanController/SentryPlanController.tsx 100.00% <100.00%> (ø)
...amPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...ers/TeamPlanController/ErrorBanner/ErrorBanner.tsx 100.00% <100.00%> (ø)
...trollers/TeamPlanController/TeamPlanController.tsx 100.00% <100.00%> (ø)
...rollers/TeamPlanController/UserCount/UserCount.tsx 100.00% <100.00%> (ø)
...pgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx 75.00% <100.00%> (ø)
... and 5 more
Components Coverage Δ
Assets 100.00% <ø> (ø)
Layouts 99.71% <ø> (ø)
Pages 98.17% <91.54%> (-0.11%) ⬇️
Services 99.35% <ø> (ø)
Shared 99.12% <ø> (ø)
UI 99.15% <ø> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8936517...79ec891. Read the comment docs.

@codecov-qa
Copy link

codecov-qa bot commented Jun 17, 2025

Codecov Report

Attention: Patch coverage is 91.54930% with 6 lines in your changes missing coverage. Please review.

Project coverage is 98.66%. Comparing base (8936517) to head (79ec891).
Report is 1 commits behind head on main.

✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...Routes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx 63.63% 4 Missing ⚠️
...PlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx 87.50% 2 Missing ⚠️
@@            Coverage Diff             @@
##             main    #3896      +/-   ##
==========================================
- Coverage   98.72%   98.66%   -0.06%     
==========================================
  Files         827      828       +1     
  Lines       15040    15071      +31     
  Branches     4306     4304       -2     
==========================================
+ Hits        14848    14870      +22     
- Misses        185      193       +8     
- Partials        7        8       +1     
Files with missing lines Coverage Δ
...roPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...ontrollers/ProPlanController/ProPlanController.tsx 100.00% <100.00%> (ø)
...trollers/ProPlanController/UserCount/UserCount.tsx 100.00% <100.00%> (ø)
...ryPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...lers/SentryPlanController/SentryPlanController.tsx 100.00% <100.00%> (ø)
...amPlanController/BillingOptions/BillingOptions.tsx 100.00% <100.00%> (ø)
...ers/TeamPlanController/ErrorBanner/ErrorBanner.tsx 100.00% <100.00%> (ø)
...trollers/TeamPlanController/TeamPlanController.tsx 100.00% <100.00%> (ø)
...rollers/TeamPlanController/UserCount/UserCount.tsx 100.00% <100.00%> (ø)
...pgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx 75.00% <100.00%> (ø)
... and 5 more
Components Coverage Δ
Assets 100.00% <ø> (ø)
Layouts 99.71% <ø> (ø)
Pages 98.17% <91.54%> (-0.11%) ⬇️
Services 99.35% <ø> (ø)
Shared 99.12% <ø> (ø)
UI 99.15% <ø> (ø)

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8936517...79ec891. Read the comment docs.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

codecov-public-qa bot commented Jun 17, 2025

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
3478 3 3475 95
View the top 3 failed test(s) by shortest run time
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.test.tsx > user is not upgrading personal org
Stack Traces | 0s run time
Error: [Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.test.tsx > user is upgrading personal org
Stack Traces | 0s run time
Error: [Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.test.tsx > BillingOptions > when rendered > planString is set to annual plan > renders annual button as "selected"
Stack Traces | 0.217s run time
Error: expect(element).toBeChecked()

Received element is not checked:
  <button
  aria-checked="false"
  class="relative flex-1 w-32"
  data-radix-collection-item=""
  data-state="unchecked"
  data-testid="radio-annual"
  id=":r0:"
  role="radio"
  tabindex="-1"
  type="button"
  value="Annual"
/>
 ❯ .../TeamPlanController/BillingOptions/BillingOptions.test.tsx:141:27

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@codecov-staging
Copy link

codecov-staging bot commented Jun 17, 2025

Bundle Report

Changes will increase total bundle size by 6.88kB (0.05%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
gazebo-staging-system 6.23MB 3.39kB (0.05%) ⬆️
gazebo-staging-esm 6.31MB 3.49kB (0.06%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: gazebo-staging-system

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/index-legacy.*.js 2.15kB 28.46kB 8.17% ⚠️
assets/index-legacy.*.js 1.35kB 26.31kB 5.39% ⚠️
assets/index-legacy.*.js -54 bytes 113.08kB -0.05%
assets/index-legacy.*.js -802 bytes 115.09kB -0.69%
assets/index-legacy.*.js -54 bytes 83.98kB -0.06%
assets/index-legacy.*.js 19 bytes 716.82kB 0.0%
assets/RawFileViewer-legacy.*.js 458 bytes 127.36kB 0.36%
assets/Checkbox-legacy.*.js (New) 1.06kB 1.06kB 100.0% 🚀
assets/OptionButton-legacy.*.js (Deleted) -735 bytes 0 bytes -100.0% 🗑️

Files in assets/index-legacy.*.js:

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.94kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PersonalOrgWarning.tsx → Total Size: 792 bytes

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateBlurb/UpdateBlurb.tsx → Total Size: 2.0kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.92kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/UserCount/UserCount.tsx → Total Size: 1.62kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx → Total Size: 1.82kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/UserCount/UserCount.tsx → Total Size: 1.63kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.91kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx → Total Size: 4.24kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/TeamPlanController.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/ErrorBanner/ErrorBanner.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PlanTypeOptions/PlanTypeOptions.tsx → Total Size: 3.81kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx → Total Size: 4.49kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/SentryPlanController.tsx → Total Size: 1.69kB

view changes for bundle: gazebo-staging-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/index.*.js -48 bytes 125.68kB -0.04%
assets/index.*.js -783 bytes 126.54kB -0.61%
assets/index.*.js 3.58kB 31.08kB 13.0% ⚠️
assets/index.*.js -48 bytes 89.24kB -0.05%
assets/RawFileViewer.*.js 459 bytes 127.15kB 0.36%
assets/index.*.css 18 bytes 101.44kB 0.02%
assets/useInvoices.*.js -5 bytes 3.21kB -0.16%
assets/Checkbox.*.js (New) 971 bytes 971 bytes 100.0% 🚀
assets/TotalsNumber.*.js -5 bytes 829 bytes -0.6%
assets/OptionButton.*.js (Deleted) -645 bytes 0 bytes -100.0% 🗑️

Files in assets/index.*.js:

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.94kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.92kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/TeamPlanController.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PersonalOrgWarning.tsx → Total Size: 792 bytes

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PlanTypeOptions/PlanTypeOptions.tsx → Total Size: 3.81kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/SentryPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/UserCount/UserCount.tsx → Total Size: 1.63kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/ErrorBanner/ErrorBanner.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx → Total Size: 1.82kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx → Total Size: 4.49kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx → Total Size: 4.24kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.91kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/UserCount/UserCount.tsx → Total Size: 1.62kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateBlurb/UpdateBlurb.tsx → Total Size: 2.0kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.tsx → Total Size: 1.69kB

Copy link

codecov bot commented Jun 17, 2025

Bundle Report

Changes will increase total bundle size by 4.31kB (0.03%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
gazebo-production-system 6.23MB 2.11kB (0.03%) ⬆️
gazebo-production-esm 6.31MB 2.2kB (0.03%) ⬆️

Affected Assets, Files, and Routes:

view changes for bundle: gazebo-production-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/index.*.js -48 bytes 89.24kB -0.05%
assets/index.*.js -1.01kB 31.08kB -3.15%
assets/index.*.js 4.59kB 32.09kB 16.68% ⚠️
assets/index.*.js -783 bytes 126.54kB -0.61%
assets/index.*.js -48 bytes 125.68kB -0.04%
assets/RawFileViewer.*.js 459 bytes 127.15kB 0.36%
assets/index.*.css 18 bytes 101.44kB 0.02%
assets/vendor_date_fns.*.js -1.37kB 28.64kB -4.56%
assets/useInvoices.*.js -5 bytes 3.21kB -0.16%
assets/Checkbox.*.js (New) 971 bytes 971 bytes 100.0% 🚀
assets/TotalsNumber.*.js -5 bytes 829 bytes -0.6%
assets/dates.*.js 82 bytes 532 bytes 18.22% ⚠️
assets/OptionButton.*.js (Deleted) -645 bytes 0 bytes -100.0% 🗑️

Files in assets/index.*.js:

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.91kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/ErrorBanner/ErrorBanner.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/SentryPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PersonalOrgWarning.tsx → Total Size: 792 bytes

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateBlurb/UpdateBlurb.tsx → Total Size: 2.0kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.92kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.94kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx → Total Size: 4.49kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx → Total Size: 4.24kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/UserCount/UserCount.tsx → Total Size: 1.63kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/UserCount/UserCount.tsx → Total Size: 1.62kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx → Total Size: 1.82kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PlanTypeOptions/PlanTypeOptions.tsx → Total Size: 3.81kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/TeamPlanController.tsx → Total Size: 1.76kB

view changes for bundle: gazebo-production-system

Assets Changed:

Asset Name Size Change Total Size Change (%)
assets/index-legacy.*.js 2.15kB 28.46kB 8.17% ⚠️
assets/index-legacy.*.js 1.35kB 26.31kB 5.39% ⚠️
assets/index-legacy.*.js -54 bytes 113.08kB -0.05%
assets/index-legacy.*.js -54 bytes 83.98kB -0.06%
assets/index-legacy.*.js 19 bytes 716.82kB 0.0%
assets/index-legacy.*.js -802 bytes 115.09kB -0.69%
assets/RawFileViewer-legacy.*.js 458 bytes 127.36kB 0.36%
assets/vendor_date_fns-legacy.*.js -1.35kB 26.34kB -4.87%
assets/Checkbox-legacy.*.js (New) 1.06kB 1.06kB 100.0% 🚀
assets/dates-legacy.*.js 71 bytes 568 bytes 14.29% ⚠️
assets/OptionButton-legacy.*.js (Deleted) -735 bytes 0 bytes -100.0% 🗑️

Files in assets/index-legacy.*.js:

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/UserCount/UserCount.tsx → Total Size: 1.62kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/ErrorBanner/ErrorBanner.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PersonalOrgWarning.tsx → Total Size: 792 bytes

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.tsx → Total Size: 4.24kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PendingUpgradeModal.tsx → Total Size: 1.82kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.92kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateBlurb/UpdateBlurb.tsx → Total Size: 2.0kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/TeamPlanController.tsx → Total Size: 1.76kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/ProPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx → Total Size: 4.49kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.94kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/SentryPlanController/SentryPlanController.tsx → Total Size: 1.69kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/PlanTypeOptions/PlanTypeOptions.tsx → Total Size: 3.81kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/BillingOptions/BillingOptions.tsx → Total Size: 2.91kB

  • ./src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/ProPlanController/UserCount/UserCount.tsx → Total Size: 1.63kB

Copy link

codecov bot commented Jun 17, 2025

❌ 3 Tests Failed:

Tests completed Failed Passed Skipped
4143 3 4140 95
View the top 3 failed test(s) by shortest run time
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.test.tsx > user is not upgrading personal org
Stack Traces | 0s run time
Error: [Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpgradeForm.test.tsx > user is upgrading personal org
Stack Traces | 0s run time
Error: [Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error
src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/Controllers/TeamPlanController/BillingOptions/BillingOptions.test.tsx > BillingOptions > when rendered > planString is set to annual plan > renders annual button as "selected"
Stack Traces | 0.217s run time
Error: expect(element).toBeChecked()

Received element is not checked:
  <button
  aria-checked="false"
  class="relative flex-1 w-32"
  data-radix-collection-item=""
  data-state="unchecked"
  data-testid="radio-annual"
  id=":r0:"
  role="radio"
  tabindex="-1"
  type="button"
  value="Annual"
/>
 ❯ .../TeamPlanController/BillingOptions/BillingOptions.test.tsx:141:27

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

@codecov-releaser
Copy link
Contributor

codecov-releaser commented Jun 17, 2025

✅ Deploy preview for gazebo ready!

Previews expire after 1 month automatically.

Storybook

Commit Created Cloud Enterprise
203e85a Tue, 17 Jun 2025 16:54:38 GMT Expired Expired
203e85a Wed, 18 Jun 2025 15:38:15 GMT Expired Expired
55ee73d Thu, 19 Jun 2025 19:09:07 GMT Expired Expired
9dd285b Wed, 25 Jun 2025 14:49:54 GMT Expired Expired
de013c7 Wed, 25 Jun 2025 15:05:50 GMT Expired Expired
79ec891 Wed, 25 Jun 2025 16:08:19 GMT Cloud Enterprise

Copy link
Contributor

seer-by-sentry bot commented Jun 17, 2025

Sentry detected 1 potential issue in your recent changes

There's a suspicion that `UpdateButton.tsx` at line 78 might encounter an unexpected `TypeError`. This could happen if `getNextBillingDate` returns `null` due to incomplete `accountDetails`, and the non-null assertion operator (`!`) is used, leading to a runtime issue when a string is expected.
  • Description: The bug occurs when the getNextBillingDate function returns null. This can happen if the accountDetails object or its nested properties, such as subscriptionDetail.latestInvoice.periodEnd, are missing or undefined. The code then uses the non-null assertion operator (!) on this null value. While ! is a TypeScript compile-time feature, it offers no runtime protection. Consequently, at runtime, attempting to pass this null value where a string is expected (e.g., to a component expecting a string prop) will result in a TypeError. For instance, if accountDetails is { subscriptionDetail: null }, getNextBillingDate yields null. The line nextBillingDate={getNextBillingDate(accountDetails)!} then attempts to use null as a string, leading to a runtime TypeError.
  • Code location: src/pages/PlanPage/subRoutes/UpgradePlanPage/UpgradeForm/UpdateButton/UpdateButton.tsx:78
  • Suggested fix: Remove the non-null assertion operator (!) from nextBillingDate={getNextBillingDate(accountDetails)!}. Handle the null case gracefully by providing a default value, conditional rendering, or ensuring the UpdateBlurb component can accept null or undefined for nextBillingDate. Alternatively, ensure the modal is not rendered if accountDetails is incomplete.

Did you find this useful? React with a 👍 or 👎

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IMO this is the worst area of this codebase - I'm not going to fix it though because I don't have time 🙃

Pls just review for anything obvious and let's ship it 🚢

Copy link
Contributor

On it! We are reviewing the PR and will provide feedback shortly.

Copy link
Contributor

PR Description

This pull request refactors the upgrade form to improve user experience and code maintainability. It introduces a step-by-step approach, enhances visual clarity, and reduces code duplication.

Click to see more

Key Technical Changes

  1. Replaces OptionButton with RadioTileGroup for a more visually appealing and user-friendly selection of billing cycles and plan types.
  2. Implements a Card-based layout using the ui/Card component to structure the form into distinct sections for plan selection, billing options, seat count, and price callout.
  3. Introduces a confirmation modal using ui/Modal and ui/Checkbox to ensure users review and accept the plan changes before submission.
  4. Adds a PersonalOrgWarning component to alert users when upgrading their personal organization.
  5. Renames UpgradeFormModal to PendingUpgradeModal to better reflect its purpose of handling pending upgrades awaiting payment method verification.
  6. Adds step numbers to headings to guide users through the upgrade process.

Architecture Decisions

  1. Adopts a Card-based layout to visually separate the different steps of the upgrade process, improving clarity and user guidance.
  2. Replaces the custom OptionButton with the more flexible and visually consistent RadioTileGroup component from the UI library.
  3. Introduces a confirmation modal to prevent accidental upgrades and ensure users are fully aware of the changes they are making.

Dependencies and Interactions

  1. Relies on the ui/RadioTileGroup, ui/Card, ui/Modal, ui/Checkbox, and ui/Avatar components from the UI library.
  2. Interacts with the useAvailablePlans, usePlanData, useUnverifiedPaymentMethods, and useUpgradeControls hooks to fetch plan information, user data, and handle the upgrade process.
  3. Depends on the shared/utils/billing and shared/utils/upgradeForm modules for billing calculations and form validation.

Risk Considerations

  1. The removal of the <form> element wrapping the entire card content in UpgradeForm.tsx could impact accessibility and form submission behavior. It's crucial to ensure proper form semantics are maintained.
  2. The setShowConfirmationModal(false) call inside the submit onClick handler in UpdateButton.tsx might cause the modal to close before the form submission completes, potentially creating a race condition. The modal state handling should be reviewed.
  3. The introduction of new components and layout changes requires thorough testing to ensure compatibility across different browsers and devices.

Notable Implementation Details

  1. The ErrorBanner component in TeamPlanController now updates the URL parameters to reflect the selected plan, ensuring the tier selector is updated correctly.
  2. The UserCount component now correctly handles singular/plural forms for the number of members.
  3. The confirmation modal in UpdateButton includes a checkbox to ensure users explicitly accept the plan changes before proceeding.

Comment on lines 67 to 95
<h3 className="font-semibold">Step 2: Choose a billing cycle</h3>
<div className="inline-flex items-center gap-2">
<OptionButton
type="button"
active={option}
onChange={({ text }) => {
if (text === TimePeriods.ANNUAL) {
<RadioTileGroup
value={option}
onValueChange={(value: OptionPeriod) => {
if (value === TimePeriods.ANNUAL) {
setFormValue('newPlan', proPlanYear)
} else {
setFormValue('newPlan', proPlanMonth)
}

setOption(text)
setOption(value)
}}
options={
[
{
text: TimePeriods.ANNUAL,
},
{
text: TimePeriods.MONTHLY,
},
] as const
}
/>
>
<RadioTileGroup.Item value={TimePeriods.ANNUAL} className="w-32">
<RadioTileGroup.Label>{TimePeriods.ANNUAL}</RadioTileGroup.Label>
</RadioTileGroup.Item>
<RadioTileGroup.Item value={TimePeriods.MONTHLY} className="w-32">
<RadioTileGroup.Label>{TimePeriods.MONTHLY}</RadioTileGroup.Label>
</RadioTileGroup.Item>
</RadioTileGroup>
Copy link
Contributor

Choose a reason for hiding this comment

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

The RadioTileGroup implementation looks good, but consider extracting the common billing options structure since this pattern is repeated across ProPlan, SentryPlan, and TeamPlan controllers. This would reduce code duplication and make maintenance easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You are correct, but that is out of scope for this change.

Comment on lines +41 to +43
<hr />
{/* if you're reading this, I'm sorry */}
<Card.Content>
Copy link
Contributor

Choose a reason for hiding this comment

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

The comment {/* if you're reading this, I'm sorry */} on line 42 suggests this code structure might not be ideal. Consider refactoring this section to be more maintainable. Also, the <hr /> element should probably be styled with CSS classes for consistency with the design system.

Suggested change
<hr />
{/* if you're reading this, I'm sorry */}
<Card.Content>
<Card.Content>
<div className="border-t border-ds-gray-secondary" />
</Card.Content>

Comment on lines +37 to 42
const hasOneMember = activatedUserCount + inactiveUserCount === 1
return (
<p>
Your organization has {activatedUserCount + inactiveUserCount} members.
Your organization has {activatedUserCount + inactiveUserCount} member
{hasOneMember ? '' : 's'}.
</p>
Copy link
Contributor

Choose a reason for hiding this comment

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

Good improvement with the singular/plural handling! However, consider using a more robust pluralization utility if this pattern is used elsewhere in the codebase. Libraries like pluralize or a custom utility function could handle edge cases better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Seems too heavy for our usecase. I can't think of an edge case this doesn't handle

Copy link
Contributor

Choose a reason for hiding this comment

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

oh hey, I made a PR against this repo plurals/pluralize#130

Comment on lines +63 to +64
// without this line, the tier selector won't update
updateParams({ plan: TierNames.PRO })
Copy link
Contributor

Choose a reason for hiding this comment

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

The import of TierNames should be grouped with other imports from the same module. Also, the comment about updating the tier selector suggests tight coupling that might benefit from refactoring to use a more centralized state management approach.

Suggested change
// without this line, the tier selector won't update
updateParams({ plan: TierNames.PRO })
// Update tier selector to reflect the plan change
updateParams({ plan: TierNames.PRO })

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair but I've decided that is out of scope here.

Comment on lines +43 to 60
<ul className="list-inside list-disc">
{diffPlanType && (
<li className="pl-2">{`You are changing from the ${
currentIsFree ? 'Developer' : currentIsTeam ? 'Team' : 'Pro'
} plan to the [${selectedIsTeam ? 'Team' : 'Pro'} plan]`}</li>
)}
{diffSeats && (
<li className="pl-2">{`You are changing seats from ${currentPlan?.planUserCount} to [${seats}]`}</li>
)}
{diffBillingType && !currentIsFree && (
<li className="pl-2">{`You are changing your billing cycle from ${
currentIsAnnual ? 'Annual' : 'Monthly'
} to [${currentIsAnnual ? 'Monthly' : 'Annual'}]`}</li>
)}
</ul>
<br />

<h3 className="font-medium">
Copy link
Contributor

Choose a reason for hiding this comment

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

Good improvement by wrapping the list items in a proper <ul> element! This enhances accessibility and semantic HTML structure.

Comment on lines 46 to 124

return (
<div className="inline-flex">
<Button
data-cy="plan-page-plan-update"
disabled={disabled}
type="submit"
variant="primary"
hook="submit-upgrade"
to={undefined}
>
{planData?.plan?.isFreePlan ? 'Proceed to checkout' : 'Update'}
</Button>
</div>
<>
<div className="inline-flex">
<Button
type="button"
data-cy="plan-page-plan-update"
disabled={disabled}
variant="primary"
hook="confirm-upgrade"
onClick={() => {
if (!disabled) {
setShowConfirmationModal(true)
}
}}
isLoading={isLoading}
>
{planData?.plan?.isFreePlan ? 'Proceed to checkout' : 'Update'}
</Button>
</div>
<Modal
isOpen={showConfirmationModal}
onClose={() => setShowConfirmationModal(false)}
body={
<div className="flex flex-col gap-4 px-4 py-2">
<div className="inline-flex items-center gap-1">
By proceeding, you are making the following changes to your plan:
</div>
<UpdateBlurb
currentPlan={planData?.plan}
newPlan={newPlan}
seats={Number(seats)}
nextBillingDate={getNextBillingDate(accountDetails)!}
/>
<PersonalOrgWarning />
<div className="flex items-center gap-2">
<Checkbox
id="upgrade-confirmation-checkbox"
checked={confirmationIsChecked}
onClick={() => setConfirmationIsChecked(!confirmationIsChecked)}
/>
<label
className="flex flex-wrap items-center"
htmlFor="upgrade-confirmation-checkbox"
>
I accept the changes to
<div className="flex items-center gap-1 pl-1">
<Avatar user={ownerData} className="size-4" border="dark" />
<span className="font-bold">{owner}</span>
</div>
&apos;s plan.
</label>
</div>
<div className="flex justify-end gap-2">
<Button
type="button"
hook="cancel-upgrade"
variant="default"
onClick={() => setShowConfirmationModal(false)}
>
Go back
</Button>
<Button
type="submit"
hook="submit-upgrade"
variant="primary"
disabled={!confirmationIsChecked}
onClick={() => {
onSubmit()
setShowConfirmationModal(false)
}}
>
{planData?.plan?.isFreePlan ? 'Proceed to checkout' : 'Update'}
</Button>
</div>
</div>
}
title="Review plan changes"
Copy link
Contributor

Choose a reason for hiding this comment

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

The confirmation modal logic is complex and might benefit from being extracted into a custom hook like useUpgradeConfirmation. This would improve testability and reusability.

Comment on lines +108 to +113
<Button
type="submit"
hook="submit-upgrade"
variant="primary"
disabled={!confirmationIsChecked}
Copy link
Contributor

Choose a reason for hiding this comment

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

The setShowConfirmationModal(false) call inside the submit onClick handler might cause the modal to close before the form submission completes, potentially creating a race condition. Consider handling the modal state in the parent component or after the submission is complete.

Suggested change
<Button
type="submit"
hook="submit-upgrade"
variant="primary"
disabled={!confirmationIsChecked}
onClick={() => {
setConfirmationIsChecked(false)
onSubmit()
// Modal will be closed by parent component after successful submission
}}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The submission and error states are handled by the parent, it's expected that the modal will close right away regardless of successful submission. There is a new spinner on the form submit button to indicate the loading state in the parent.

Comment on lines +131 to +170
<form className="text-ds-gray-default">
<Card.Content>
<PlanTypeOptions
setFormValue={setFormValue}
setSelectedPlan={setSelectedPlan}
newPlan={newPlan}
/>
</Card.Content>
<hr />
<Controller
setSelectedPlan={setSelectedPlan}
newPlan={newPlan}
seats={seats}
setFormValue={setFormValue}
register={register}
errors={errors}
/>
)}
</form>
<Card.Content className="flex flex-col gap-6 pb-6">
<PersonalOrgWarning />
<UpdateButton
isValid={isValid}
newPlan={newPlan}
seats={seats}
onSubmit={onSubmit}
isLoading={isUpgrading}
/>
{showPendingUpgradeModal && formData ? (
<PendingUpgradeModal
isOpen={showPendingUpgradeModal}
onClose={() => setShowPendingUpgradeModal(false)}
onConfirm={() => {
setIsUpgrading(true)
upgradePlan(formData)
}}
url={unverifiedPaymentMethods?.[0]?.hostedVerificationUrl || ''}
isUpgrading={isUpgrading}
/>
) : null}
</Card.Content>
</form>
Copy link
Contributor

Choose a reason for hiding this comment

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

The form is no longer wrapped in a <form> element, which could impact accessibility and form submission behavior. Consider wrapping the entire card content in a form element or ensuring proper form semantics are maintained.

Comment on lines 9 to 30

export function PersonalOrgWarning() {
const { owner } = useParams<URLParams>()
const { data } = useUser()
const username = data?.user.username

if (owner !== username) {
return null
}

return (
<Alert variant="info">
<Alert.Title>
You&apos;re about to upgrade your personal organization:{' '}
<span className="font-bold">{username}</span>
</Alert.Title>
<Alert.Description>
If you&apos;d like to upgrade a different organization, click on the
organization selector at the top left of the page.
</Alert.Description>
</Alert>
)
Copy link
Contributor

Choose a reason for hiding this comment

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

The component correctly uses early return for better readability. However, consider memoizing this component with React.memo since it only depends on URL params and user data, which don't change frequently.

Copy link
Member

@ElioDiNino ElioDiNino left a comment

Choose a reason for hiding this comment

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

LGTM (from a functionality perspective, I am not a React expert), huge improvement 🚀

@spalmurray spalmurray force-pushed the spalmurray/checkout-improvements branch from 55ee73d to 9dd285b Compare June 25, 2025 14:45
@spalmurray spalmurray added this pull request to the merge queue Jun 25, 2025
Merged via the queue into main with commit 4ec0a46 Jun 25, 2025
52 of 64 checks passed
@spalmurray spalmurray deleted the spalmurray/checkout-improvements branch June 25, 2025 16:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants