successful-subscription.tsx 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import { useTranslation, Trans } from 'react-i18next'
  2. import { PriceExceptions } from '../shared/price-exceptions'
  3. import PremiumFeaturesLink from '../dashboard/premium-features-link'
  4. import getMeta from '../../../../utils/meta'
  5. import { useSubscriptionDashboardContext } from '../../context/subscription-dashboard-context'
  6. import OLRow from '@/shared/components/ol/ol-row'
  7. import OLCol from '@/shared/components/ol/ol-col'
  8. import OLPageContentCard from '@/shared/components/ol/ol-page-content-card'
  9. import OLNotification from '@/shared/components/ol/ol-notification'
  10. import {
  11. AI_ADD_ON_CODE,
  12. ADD_ON_NAME,
  13. isStandaloneAiPlanCode,
  14. } from '../../data/add-on-codes'
  15. import { PaidSubscription } from '../../../../../../types/subscription/dashboard/subscription'
  16. import { useBroadcastUser } from '@/shared/hooks/user-channel/use-broadcast-user'
  17. import { getUpgradePlanDisplayName } from '../../util/plan-display-names'
  18. function SuccessfulSubscription() {
  19. const { t } = useTranslation()
  20. const { personalSubscription: subscription } =
  21. useSubscriptionDashboardContext()
  22. const postCheckoutRedirect = getMeta('ol-postCheckoutRedirect')
  23. const isUpgrade = getMeta('ol-isUpgrade')
  24. const { appName, adminEmail } = getMeta('ol-ExposedSettings')
  25. useBroadcastUser()
  26. if (!subscription || !('payment' in subscription)) return null
  27. if (isUpgrade) {
  28. return (
  29. <UpgradeSuccess
  30. subscription={subscription}
  31. postCheckoutRedirect={postCheckoutRedirect}
  32. />
  33. )
  34. }
  35. const onAiStandalonePlan = isStandaloneAiPlanCode(subscription.planCode)
  36. return (
  37. <div className="container">
  38. <OLRow>
  39. <OLCol lg={{ span: 8, offset: 2 }}>
  40. <OLPageContentCard>
  41. <h2>{t('thanks_for_subscribing')}</h2>
  42. <OLNotification
  43. type="success"
  44. content={
  45. <>
  46. {subscription.payment.trialEndsAt && (
  47. <>
  48. <p>
  49. <Trans
  50. i18nKey="next_payment_of_x_collectected_on_y"
  51. values={{
  52. paymentAmmount: subscription.payment.displayPrice,
  53. collectionDate:
  54. subscription.payment.nextPaymentDueAt,
  55. }}
  56. shouldUnescape
  57. tOptions={{ interpolation: { escapeValue: true } }}
  58. components={[<strong />, <strong />]} // eslint-disable-line react/jsx-key
  59. />
  60. </p>
  61. <PriceExceptions subscription={subscription} />
  62. </>
  63. )}
  64. <div className="d-flex justify-content-between align-items-center gap-3">
  65. <span>{t('to_modify_your_subscription_go_to')}</span>
  66. <a href="/user/subscription" rel="noopener noreferrer">
  67. {t('manage_subscription')}
  68. </a>
  69. </div>
  70. </>
  71. }
  72. />
  73. {subscription.groupPlan && (
  74. <p>
  75. <a
  76. href={`/manage/groups/${subscription._id}/members`}
  77. className="btn btn-primary btn-large"
  78. >
  79. {t('invite_group_members')}
  80. </a>
  81. </p>
  82. )}
  83. <ThankYouSection
  84. subscription={subscription}
  85. onAiStandalonePlan={onAiStandalonePlan}
  86. />
  87. <PremiumFeaturesLink subscription={subscription} />
  88. <p>
  89. {t('need_anything_contact_us_at')}&nbsp;
  90. <a href={`mailto:${adminEmail}`} rel="noopener noreferrer">
  91. {adminEmail}
  92. </a>
  93. .
  94. </p>
  95. {!onAiStandalonePlan && (
  96. <p>
  97. <Trans
  98. i18nKey="help_improve_overleaf_fill_out_this_survey"
  99. components={[
  100. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  101. <a
  102. href="https://forms.gle/CdLNX9m6NLxkv1yr5"
  103. target="_blank"
  104. rel="noopener noreferrer"
  105. />,
  106. ]}
  107. />
  108. </p>
  109. )}
  110. <p>
  111. {t('regards')},
  112. <br />
  113. The {appName} Team
  114. </p>
  115. <p>
  116. <a
  117. className="btn btn-primary"
  118. href={postCheckoutRedirect || '/project'}
  119. rel="noopener noreferrer"
  120. >
  121. {t('back_to_your_projects')}
  122. </a>
  123. </p>
  124. </OLPageContentCard>
  125. </OLCol>
  126. </OLRow>
  127. </div>
  128. )
  129. }
  130. function UpgradeSuccess({
  131. subscription,
  132. postCheckoutRedirect,
  133. }: {
  134. subscription: PaidSubscription
  135. postCheckoutRedirect: string | undefined
  136. }) {
  137. const { t } = useTranslation()
  138. const planDisplayName = getUpgradePlanDisplayName(subscription.planCode, t)
  139. let benefitsText = ''
  140. if (subscription.planCode.startsWith('professional')) {
  141. benefitsText = t('get_ready_for_overleaf_at_its_best_pro')
  142. } else if (subscription.planCode.startsWith('collaborator')) {
  143. benefitsText = t('get_ready_for_overleaf_standard')
  144. }
  145. return (
  146. <div className="container">
  147. <OLRow>
  148. <OLCol lg={{ span: 8, offset: 2 }}>
  149. <OLPageContentCard>
  150. <h2>{t('welcome_to_plan', { planName: planDisplayName })}</h2>
  151. <OLNotification
  152. type="success"
  153. content={
  154. <div className="d-flex justify-content-between align-items-center gap-3">
  155. <span>{t('youve_upgraded_your_subscription')}</span>
  156. <a href="/user/subscription" rel="noopener noreferrer">
  157. {t('manage_subscription')}
  158. </a>
  159. </div>
  160. }
  161. />
  162. <p>
  163. <Trans
  164. i18nKey="next_payment_of_x_collectected_on_y"
  165. values={{
  166. paymentAmmount: subscription.payment.displayPrice,
  167. collectionDate: subscription.payment.nextPaymentDueAt,
  168. }}
  169. shouldUnescape
  170. tOptions={{ interpolation: { escapeValue: true } }}
  171. components={[<strong />, <strong />]} // eslint-disable-line react/jsx-key
  172. />
  173. </p>
  174. <PriceExceptions subscription={subscription} />
  175. {benefitsText && <p>{benefitsText}</p>}
  176. <p>
  177. <Trans
  178. i18nKey="find_out_how_to_get_the_most_out_of_your_new_subscription"
  179. components={[
  180. // eslint-disable-next-line react/jsx-key, jsx-a11y/anchor-has-content
  181. <a
  182. href="https://docs.overleaf.com/integrations-and-add-ons/ai-features"
  183. target="_blank"
  184. rel="noopener noreferrer"
  185. />,
  186. ]}
  187. />
  188. </p>
  189. <p>
  190. <a
  191. className="btn btn-primary"
  192. href={postCheckoutRedirect || '/project'}
  193. rel="noopener noreferrer"
  194. >
  195. {t('back_to_your_projects')}
  196. </a>
  197. </p>
  198. </OLPageContentCard>
  199. </OLCol>
  200. </OLRow>
  201. </div>
  202. )
  203. }
  204. function ThankYouSection({
  205. subscription,
  206. onAiStandalonePlan,
  207. }: {
  208. subscription: PaidSubscription
  209. onAiStandalonePlan: boolean
  210. }) {
  211. const { t } = useTranslation()
  212. const hasAiAddon = subscription?.addOns?.some(
  213. addOn => addOn.addOnCode === AI_ADD_ON_CODE
  214. )
  215. if (onAiStandalonePlan) {
  216. return (
  217. <p>
  218. {t('thanks_for_subscribing_to_the_add_on', {
  219. addOnName: ADD_ON_NAME,
  220. })}
  221. </p>
  222. )
  223. }
  224. if (hasAiAddon) {
  225. return (
  226. <p>
  227. {t('thanks_for_subscribing_to_plan_with_add_on', {
  228. planName: subscription.plan.name,
  229. addOnName: ADD_ON_NAME,
  230. })}
  231. </p>
  232. )
  233. }
  234. return (
  235. <p>
  236. {t('thanks_for_subscribing_you_help_sl', {
  237. planName: subscription.plan.name,
  238. })}
  239. </p>
  240. )
  241. }
  242. export default SuccessfulSubscription