Przeglądaj źródła

Merge pull request #12007 from overleaf/ii-react-subscription-dash-thank-you

[web] Successful subscription react migration

GitOrigin-RevId: 1d7d849415f4b7a7f60ddf8a4e18570ff5142196
ilkin-overleaf 3 lat temu
rodzic
commit
48587a7a62

+ 1 - 0
services/web/app/src/infrastructure/ExpressLocals.js

@@ -367,6 +367,7 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
     res.locals.ExposedSettings = {
       isOverleaf: Settings.overleaf != null,
       appName: Settings.appName,
+      adminEmail: Settings.adminEmail,
       dropboxAppName:
         Settings.apis.thirdPartyDataStore?.dropboxAppName || 'Overleaf',
       hasSamlBeta: req.session.samlBeta,

+ 4 - 0
services/web/app/views/subscriptions/successful-subscription-react.pug

@@ -3,5 +3,9 @@ extends ../layout-marketing
 block entrypointVar
 	- entrypoint = 'pages/user/subscription/successful-subscription'
 
+block append meta
+	meta(name="ol-subscription" data-type="json" content=personalSubscription)
+	meta(name="ol-postCheckoutRedirect" content=postCheckoutRedirect)
+
 block content
 	main.content.content-alt#subscription-success-root

+ 6 - 0
services/web/frontend/extracted-translations.json

@@ -28,6 +28,7 @@
   "add_or_remove_project_from_tag": "",
   "add_role_and_department": "",
   "add_to_folder": "",
+  "add_your_first_group_member_now": "",
   "adding": "",
   "additional_licenses": "",
   "address_line_1": "",
@@ -338,6 +339,7 @@
   "have_more_days_to_try": "",
   "headers": "",
   "help": "",
+  "help_improve_overleaf_fill_out_this_survey": "",
   "hide_document_preamble": "",
   "hide_outline": "",
   "history": "",
@@ -489,6 +491,7 @@
   "name": "",
   "navigate_log_source": "",
   "navigation": "",
+  "need_anything_contact_us_at": "",
   "need_more_than_x_licenses": "",
   "need_to_add_new_primary_before_remove": "",
   "need_to_leave": "",
@@ -648,6 +651,7 @@
   "refresh_page_after_linking_dropbox": "",
   "refresh_page_after_starting_free_trial": "",
   "refreshing": "",
+  "regards": "",
   "relink_your_account": "",
   "remote_service_error": "",
   "remove": "",
@@ -792,6 +796,8 @@
   "terminated": "",
   "tex_live_version": "",
   "thank_you_exclamation": "",
+  "thanks_for_subscribing": "",
+  "thanks_for_subscribing_you_help_sl": "",
   "thanks_settings_updated": "",
   "the_following_files_already_exist_in_this_project": "",
   "then_x_price_per_month": "",

+ 7 - 1
services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx

@@ -1,4 +1,6 @@
 import useWaitForI18n from '../../../../shared/hooks/use-wait-for-i18n'
+import { SubscriptionDashboardProvider } from '../../context/subscription-dashboard-context'
+import SuccessfulSubscription from './successful-subscription'
 
 function Root() {
   const { isReady } = useWaitForI18n()
@@ -7,7 +9,11 @@ function Root() {
     return null
   }
 
-  return <h2>React Subscription Success</h2>
+  return (
+    <SubscriptionDashboardProvider>
+      <SuccessfulSubscription />
+    </SubscriptionDashboardProvider>
+  )
 }
 
 export default Root

+ 108 - 0
services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx

@@ -0,0 +1,108 @@
+import { useTranslation, Trans } from 'react-i18next'
+import { Col, Row } from 'react-bootstrap'
+import { PriceExceptions } from '../shared/price-exceptions'
+import PremiumFeaturesLink from '../dashboard/premium-features-link'
+import getMeta from '../../../../utils/meta'
+import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
+import { ExposedSettings } from '../../../../../../types/exposed-settings'
+
+function SuccessfulSubscription() {
+  const { t } = useTranslation()
+  const { personalSubscription: subscription } =
+    useSubscriptionDashboardContext()
+  const postCheckoutRedirect = getMeta('ol-postCheckoutRedirect') as
+    | string
+    | undefined
+  const { appName, adminEmail }: ExposedSettings = getMeta('ol-ExposedSettings')
+
+  if (!subscription || !('recurly' in subscription)) return null
+
+  return (
+    <div className="container">
+      <Row>
+        <Col md={8} mdOffset={2}>
+          <div className="card">
+            <div className="page-header">
+              <h2>{t('thanks_for_subscribing')}</h2>
+            </div>
+            <div role="alert" className="alert alert-success">
+              {subscription.recurly.trial_ends_at && (
+                <>
+                  <p>
+                    <Trans
+                      i18nKey="next_payment_of_x_collectected_on_y"
+                      values={{
+                        paymentAmmount: subscription.recurly.displayPrice,
+                        collectionDate: subscription.recurly.nextPaymentDueAt,
+                      }}
+                      components={[<strong />, <strong />]} // eslint-disable-line react/jsx-key
+                    />
+                  </p>
+                  <PriceExceptions subscription={subscription} />
+                </>
+              )}
+              <p>
+                {t('to_modify_your_subscription_go_to')}&nbsp;
+                <a href="/user/subscription" rel="noopener noreferrer">
+                  {t('manage_subscription')}.
+                </a>
+              </p>
+            </div>
+            {subscription.groupPlan && (
+              <p>
+                <a
+                  href={`/manage/groups/${subscription._id}/members`}
+                  className="btn btn-primary btn-large"
+                >
+                  {t('add_your_first_group_member_now')}
+                </a>
+              </p>
+            )}
+            <p>
+              {t('thanks_for_subscribing_you_help_sl', {
+                planName: subscription.plan.name,
+              })}
+            </p>
+            <PremiumFeaturesLink />
+            <p>
+              {t('need_anything_contact_us_at')}&nbsp;
+              <a href={`mailto:${adminEmail}`} rel="noopener noreferrer">
+                {adminEmail}
+              </a>
+              .
+            </p>
+            <p>
+              <Trans
+                i18nKey="help_improve_overleaf_fill_out_this_survey"
+                components={[
+                  // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
+                  <a
+                    href="https://forms.gle/CdLNX9m6NLxkv1yr5"
+                    target="_blank"
+                    rel="noopener noreferrer"
+                  />,
+                ]}
+              />
+            </p>
+            <p>
+              {t('regards')},
+              <br />
+              The {appName} Team
+            </p>
+            <p>
+              <a
+                className="btn btn-primary"
+                href={postCheckoutRedirect || '/project'}
+                rel="noopener noreferrer"
+              >
+                &lt; {t('back_to_your_projects')}
+              </a>
+            </p>
+          </div>
+        </Col>
+      </Row>
+    </div>
+  )
+}
+
+export default SuccessfulSubscription

+ 1 - 0
services/web/frontend/stories/decorators/scope.tsx

@@ -124,6 +124,7 @@ const initialize = () => {
   window.user = user
 
   window.ExposedSettings = {
+    adminEmail: 'placeholder@example.com',
     appName: 'Overleaf',
     cookieDomain: '.overleaf.stories',
     dropboxAppName: 'Overleaf-Stories',

+ 84 - 0
services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx

@@ -0,0 +1,84 @@
+import { expect } from 'chai'
+import { screen, within } from '@testing-library/react'
+import SuccessfulSubscription from '../../../../../../frontend/js/features/subscription/components/successful-subscription/successful-subscription'
+import { renderWithSubscriptionDashContext } from '../../helpers/render-with-subscription-dash-context'
+import { annualActiveSubscription } from '../../fixtures/subscriptions'
+
+describe('successful subscription page', function () {
+  afterEach(function () {
+    window.metaAttributesCache = new Map()
+  })
+
+  it('renders the invoices link', function () {
+    const adminEmail = 'foo@example.com'
+    const options = {
+      metaTags: [
+        {
+          name: 'ol-ExposedSettings',
+          value: {
+            adminEmail,
+          },
+        },
+        { name: 'ol-subscription', value: annualActiveSubscription },
+      ],
+    }
+    renderWithSubscriptionDashContext(<SuccessfulSubscription />, options)
+
+    screen.getByRole('heading', { name: /thanks for subscribing/i })
+    const alert = screen.getByRole('alert')
+    within(alert).getByText(/to modify your subscription go to/i)
+    const manageSubscriptionLink = within(alert).getByRole('link', {
+      name: /manage subscription/i,
+    })
+    expect(manageSubscriptionLink.getAttribute('href')).to.equal(
+      '/user/subscription'
+    )
+    screen.getByText(
+      `Thank you for subscribing to the ${annualActiveSubscription.plan.name} plan.`,
+      { exact: false }
+    )
+    screen.getByText(
+      /it’s support from people like yourself that allows .* to continue to grow and improve/i
+    )
+    expect(screen.getByText(/get the most out of your/i).textContent).to.match(
+      /get the most out of your .* subscription by checking out the list of .*’s premium features/i
+    )
+    expect(
+      screen
+        .getByText(/if there is anything you ever/i)
+        .textContent?.replace(/\xA0/g, ' ')
+    ).to.equal(
+      `If there is anything you ever need please feel free to contact us directly at ${adminEmail}.`
+    )
+
+    const contactLink = screen.getByRole('link', {
+      name: adminEmail,
+    })
+    expect(contactLink.getAttribute('href')).to.equal(`mailto:${adminEmail}`)
+
+    expect(
+      screen.getByText(/if you would like to help us improve/i).textContent
+    ).to.match(
+      /if you would like to help us improve .*, please take a moment to fill out this survey/i
+    )
+
+    const surveyLink = screen.getByRole('link', {
+      name: /this survey/i,
+    })
+    expect(surveyLink.getAttribute('href')).to.equal(
+      'https://forms.gle/CdLNX9m6NLxkv1yr5'
+    )
+
+    const helpLink = screen.getByRole('link', {
+      name: /.*’s premium features/i,
+    })
+    expect(helpLink.getAttribute('href')).to.equal(
+      '/learn/how-to/Overleaf_premium_features'
+    )
+
+    const backToYourProjectsLink = screen.getByRole('link', {
+      name: /back to your projects/i,
+    })
+    expect(backToYourProjectsLink.getAttribute('href')).to.equal('/project')
+  })
+})

+ 1 - 0
services/web/types/exposed-settings.ts

@@ -5,6 +5,7 @@ type TemplateLink = {
 }
 
 export type ExposedSettings = {
+  adminEmail: string
   appName: string
   cookieDomain: string
   dropboxAppName: string