Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

PLG: migrate subscription management page#62754

Merged
taras-yemets merged 50 commits into
mainfrom
ty/plg-subscription-management-page
May 30, 2024
Merged

PLG: migrate subscription management page#62754
taras-yemets merged 50 commits into
mainfrom
ty/plg-subscription-management-page

Conversation

@taras-yemets

@taras-yemets taras-yemets commented May 17, 2024

Copy link
Copy Markdown
Contributor

This PR migrates the Cody subscription management page from SSC to the /cody/subscription/manage path on dotcom. The page is available only if the embedded Cody Pro UI is enabled.

Fixes https://github.com/sourcegraph/self-serve-cody/issues/513

2-min Loom

Implementation Details:

  1. Added a refetch function to the useApiCaller hook to allow resource refetching after mutations.
  2. Changed Date types to string in the Invoice and Subscription interfaces, partially addressing this discussion.

UI Updates:

The UI was implemented based on Figma designs with some adjustments (cc: @rrhyne):

  1. Button Styling: "Cancel subscription" and other secondary buttons now use @sourcegraph/wilcard components, resulting in some style differences (e.g., background color) from the Figma design.
  2. Stripe Form Inputs: Styled to match the @sourcegraph/wilcard form fields.
  3. Async Action Buttons: Buttons triggering asynchronous actions (cancel subscription, renew subscription, and save changes) now include icons (cancel, refresh, and check, respectively). During async operations, these icons are replaced by loading spinners. Since Figma doesn't specify loading states for these buttons/forms, submit buttons are disabled and display loading indicators when actions are in progress.
Screenshots
Description Light Dark
Active subscription Screenshot 2024-05-24 at 17 02 52Screenshot 2024-05-24 at 17 03 05Screenshot 2024-05-24 at 17 03 12Screenshot 2024-05-24 at 17 03 16Screenshot 2024-05-24 at 17 03 26 Screenshot 2024-05-24 at 17 04 41Screenshot 2024-05-24 at 17 04 58
Canceled subscription Screenshot 2024-05-24 at 17 03 48 Screenshot 2024-05-24 at 17 04 33

Test plan

  • CI
  • Tested manually
    • Run Sourcegraph in the enterprise mode (sg start)
      • navigate to /cody/subscription/manage - page should not be rendered
    • Run Sourcegraph in dotcom mode (sg start dotcom)
      • Navigate to /cody/manage page
        • user is a subscription admin
          • "Manage subscription" link is rendered
          • Link click navigates to /cody/subscription/manage route
          • Ensure Subscription management page is rendered
          • Ensure user can change payment method
          • Ensure user can change billing address
          • Ensure user can cancel and renew subscription
          • Ensure invoices are listed and can be downloaded
        • user is not admin
          • "Manage subscription" link is not rendered
          • navigate to /cody/subscription/manage by changing the URL in browser
          • Ensure Subscription management page is not rendered and user is redirected to the New subscription page

@cla-bot cla-bot Bot added the cla-signed label May 17, 2024
@taras-yemets taras-yemets requested review from rrhyne May 24, 2024 14:16

@vdavid vdavid left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TBH, I didn't do a deep-review on the large new components because they didn't have a diff with the old ones (as they are in another repo), but I did skim through them and nothing stood out as wrong.

Great job overall, this must've been a lot of work with many small details to get right. Thanks for the small improvements everywhere!

Comment on lines +135 to +136
to="/cody/subscription/manage"
onClick={() => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

That is more concise indeed, thanks. 👍

Comment thread client/web/src/cody/management/api/hooks/useApiClient.tsx Outdated
return <Navigate to="/cody/manage/subscription/new" replace={true} />
}

return <PageContent />

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I like how the edge-cases are handled here before fetching the subscription. Maybe leave a comment here or on PageContent that it's separated to avoid a costly API call if we don't need it? Otherwise, someone might be inclined to merge the two because none of the two components are that large.

)

/* eslint-disable react/forbid-dom-props */
const CodyIcon: React.FC<{ className?: string }> = ({ className }) => (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would this work instead of inlining the svg in this component?

If so, maybe also extract BackIcon to its own file next to CodyIcon.tsx for better discoverability?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Would this work instead of inlining the svg in this component?

No, it appeared to be a different one.

maybe also extract BackIcon to its own file next to CodyIcon.tsx for better discoverability?

It's used in one place so I colocated definition and usage. If we are going to reuse it, we should move it to a separate location.

try {
const callerResponse = await caller.call(call)
const callApi = useCallback(async () => {
setLoading(false)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you think the ignore thing was unnecessary in the first place? If it was necessary, what change made it unnecessary? (This is a genuine question on my side, I don't have the answer. I think I hadn't seen this ignore pattern in React before and when I saw it here, I thought "there must be a more concise solution than this" but I couldn't find a better one. I'm curious if you thought about it and what the solution is as you see it.)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I thought about the current approach (custom data fetching hook) as I was migrating the Manage Subscription page.

It works without ignore, but is not reliable for this and a bunch of other reasons:

  1. Re-fetching After Mutations: Multiple components may mutate a resource and then trigger a re-fetch. The current approach might not handle all edge cases and side effects effectively.
  2. Resource Sharing Across Components: When multiple components need the same resource, we either:
  • Call useApiCaller() in each component, resulting in multiple API calls. Although caching can mitigate this, implementing it reliably can be complex.
  • Prop-drill the data (as done in this PR), which is not scalable either.

I recommend using a third-party library to manage server state. I've previously used React Query, which addresses these issues and many others out of the box. React Query's useQuery and useMutation hooks are similar to Apollo's, making them familiar to our team.

I plan to create a PR demonstrating the difference in approaches using React Query.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

justify-content: space-between;
align-items: center;

&-col {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TIL this works. I thought only &.something was allowed, never tried it without the dot. Thanks for teaching me this :)

//
// It's hard to do async hook testing correctly. This might be helpful:
// https://react-hooks-testing-library.com/usage/advanced-hooks#async
class FakeCaller implements Caller {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Refactored to use vitest mocking utilities instead of custom ones.
cc: @chrsmith

@rrhyne

rrhyne commented May 29, 2024

Copy link
Copy Markdown

Thank you @taras-yemets! I've created a QA page with some small changes here:

QA notes in Figma

I haven't addressed a few things in the old design light address display to keep the scope low.

One QQ though, how difficult would it be to add a remove credit card button? If it adds all kinds of hidden scope, forget it, but if it's a small API call and the system handles the ramifications, it would help establish more trust with users.

@taras-yemets

Copy link
Copy Markdown
Contributor Author

Thanks for your review, @rrhyne!
I addressed the feedback already.

how difficult would it be to add a remove credit card button

It's simple from the frontend perspective, but not sure about the backend.
@vdavid, @chrsmith, can a pro subscription exist without a payment method?

@taras-yemets taras-yemets merged commit 03bec9c into main May 30, 2024
@taras-yemets taras-yemets deleted the ty/plg-subscription-management-page branch May 30, 2024 12:07
@vdavid

vdavid commented May 30, 2024

Copy link
Copy Markdown
Contributor

@taras-yemets, I think the back end (our DB, Go code, and Stripe) can handle it, but the front end as a whole must be prepared for the related use case of re-adding a payment method.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants