diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js index f73ccd6f07..7d1ed633b6 100644 --- a/services/web/app/src/infrastructure/ExpressLocals.js +++ b/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, diff --git a/services/web/app/views/subscriptions/successful-subscription-react.pug b/services/web/app/views/subscriptions/successful-subscription-react.pug index 8403c167e9..fbe2767141 100644 --- a/services/web/app/views/subscriptions/successful-subscription-react.pug +++ b/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 diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 610c4d5c51..55fe1b2d47 100644 --- a/services/web/frontend/extracted-translations.json +++ b/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": "", diff --git a/services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx b/services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx index d9f7c16d99..3fa8fbb0f3 100644 --- a/services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx +++ b/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

React Subscription Success

+ return ( + + + + ) } export default Root diff --git a/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx b/services/web/frontend/js/features/subscription/components/successful-subscription/successful-subscription.tsx new file mode 100644 index 0000000000..5b4b07934b --- /dev/null +++ b/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 ( +
+ + +
+
+

{t('thanks_for_subscribing')}

+
+
+ {subscription.recurly.trial_ends_at && ( + <> +

+ , ]} // eslint-disable-line react/jsx-key + /> +

+ + + )} +

+ {t('to_modify_your_subscription_go_to')}  + + {t('manage_subscription')}. + +

+
+ {subscription.groupPlan && ( +

+ + {t('add_your_first_group_member_now')} + +

+ )} +

+ {t('thanks_for_subscribing_you_help_sl', { + planName: subscription.plan.name, + })} +

+ +

+ {t('need_anything_contact_us_at')}  + + {adminEmail} + + . +

+

+ , + ]} + /> +

+

+ {t('regards')}, +
+ The {appName} Team +

+

+ + < {t('back_to_your_projects')} + +

+
+ +
+
+ ) +} + +export default SuccessfulSubscription diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index e277a54f99..f4ae1dbc89 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/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', diff --git a/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx b/services/web/test/frontend/features/subscription/components/successful-subscription/successful-subscription.test.tsx new file mode 100644 index 0000000000..3cad154062 --- /dev/null +++ b/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(, 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') + }) +}) diff --git a/services/web/types/exposed-settings.ts b/services/web/types/exposed-settings.ts index 7cf39cffcf..5c75a9ca63 100644 --- a/services/web/types/exposed-settings.ts +++ b/services/web/types/exposed-settings.ts @@ -5,6 +5,7 @@ type TemplateLink = { } export type ExposedSettings = { + adminEmail: string appName: string cookieDomain: string dropboxAppName: string