Jelajahi Sumber

Merge pull request #11963 from overleaf/ii-react-subscription-dash-reactivate-subscription

[web] Subscription dash reactivate button react migration

GitOrigin-RevId: dde8dd1abb8979bdf90d71ea07e1336e9af491b3
ilkin-overleaf 3 tahun lalu
induk
melakukan
612728d300

+ 31 - 0
services/web/frontend/js/features/subscription/components/dashboard/reactivate-subscription.tsx

@@ -0,0 +1,31 @@
+import { useTranslation } from 'react-i18next'
+import { postJSON } from '../../../../infrastructure/fetch-json'
+import { reactivateSubscriptionUrl } from '../../data/subscription-url'
+import { reload } from '../../../../shared/components/location'
+import useAsync from '../../../../shared/hooks/use-async'
+
+function ReactivateSubscription() {
+  const { t } = useTranslation()
+  const { isLoading, isSuccess, runAsync } = useAsync()
+
+  const handleReactivate = () => {
+    runAsync(postJSON(reactivateSubscriptionUrl)).catch(console.error)
+  }
+
+  if (isSuccess) {
+    reload()
+  }
+
+  return (
+    <button
+      type="button"
+      className="btn btn-primary"
+      disabled={isLoading || isSuccess}
+      onClick={handleReactivate}
+    >
+      {t('reactivate_subscription')}
+    </button>
+  )
+}
+
+export default ReactivateSubscription

+ 2 - 6
services/web/frontend/js/features/subscription/components/dashboard/states/canceled.tsx

@@ -1,6 +1,7 @@
 import { useTranslation, Trans } from 'react-i18next'
 import { RecurlySubscription } from '../../../../../../../types/subscription/dashboard/subscription'
 import PremiumFeaturesLink from '../premium-features-link'
+import ReactivateSubscription from '../reactivate-subscription'
 
 export function CanceledSubscription({
   subscription,
@@ -46,12 +47,7 @@ export function CanceledSubscription({
           {t('view_your_invoices')}
         </a>
       </p>
-      <form action="/user/subscription/reactivate" method="POST">
-        <input type="hidden" name="_csrf" value={window.csrfToken} />
-        <button type="submit" className="btn btn-primary">
-          {t('reactivate_subscription')}
-        </button>
-      </form>
+      <ReactivateSubscription />
     </>
   )
 }

+ 1 - 0
services/web/frontend/js/features/subscription/data/subscription-url.ts

@@ -4,3 +4,4 @@ export const cancelPendingSubscriptionChangeUrl =
 export const cancelSubscriptionUrl = '/user/subscription/cancel'
 export const redirectAfterCancelSubscriptionUrl = '/user/subscription/canceled'
 export const extendTrialUrl = '/user/subscription/extend'
+export const reactivateSubscriptionUrl = '/user/subscription/reactivate'

+ 34 - 0
services/web/test/frontend/features/subscription/components/dashboard/personal-subscription.test.tsx

@@ -16,7 +16,10 @@ import {
   cleanUpContext,
   renderWithSubscriptionDashContext,
 } from '../../helpers/render-with-subscription-dash-context'
+import { reactivateSubscriptionUrl } from '../../../../../../frontend/js/features/subscription/data/subscription-url'
+import * as locationModule from '../../../../../../frontend/js/shared/components/location'
 import fetchMock from 'fetch-mock'
+import sinon from 'sinon'
 
 describe('<PersonalSubscription />', function () {
   afterEach(function () {
@@ -78,6 +81,37 @@ describe('<PersonalSubscription />', function () {
       screen.getByRole('button', { name: 'Reactivate your subscription' })
     })
 
+    it('reactivates canceled plan', async function () {
+      const reload = sinon.stub(locationModule, 'reload')
+
+      renderWithSubscriptionDashContext(<PersonalSubscription />, {
+        metaTags: [{ name: 'ol-subscription', value: canceledSubscription }],
+      })
+
+      const reactivateBtn = screen.getByRole<HTMLButtonElement>('button', {
+        name: 'Reactivate your subscription',
+      })
+
+      // 1st click - fail
+      fetchMock.postOnce(reactivateSubscriptionUrl, 400)
+      fireEvent.click(reactivateBtn)
+      expect(reactivateBtn.disabled).to.be.true
+      await fetchMock.flush(true)
+      expect(reactivateBtn.disabled).to.be.false
+      expect(reload).not.to.have.been.called
+      fetchMock.reset()
+
+      // 2nd click - success
+      fetchMock.postOnce(reactivateSubscriptionUrl, 200)
+      fireEvent.click(reactivateBtn)
+      await fetchMock.flush(true)
+      expect(reload).to.have.been.calledOnce
+      expect(reactivateBtn.disabled).to.be.true
+      fetchMock.reset()
+
+      reload.restore()
+    })
+
     it('renders the expired dash', function () {
       renderWithSubscriptionDashContext(<PersonalSubscription />, {
         metaTags: [

+ 2 - 0
services/web/test/frontend/features/subscription/fixtures/subscriptions.tsx

@@ -475,6 +475,7 @@ export const trialCollaboratorSubscription: RecurlySubscription = {
     trial_ends_at: new Date(sevenDaysFromToday).toString(),
     activeCoupons: [],
     account: {
+      email: 'foo@example.com',
       has_canceled_subscription: {
         _: 'false',
         $: {
@@ -523,6 +524,7 @@ export const monthlyActiveCollaborator: RecurlySubscription = {
     trial_ends_at: null,
     activeCoupons: [],
     account: {
+      email: 'foo@example.com',
       has_canceled_subscription: { _: 'false', $: { type: 'boolean' } },
       has_past_due_invoice: { _: 'false', $: { type: 'boolean' } },
     },