Просмотр исходного кода

Adding BRL geo pricing (#17164)

* BRL geo pricing banners and modals

* format:fix

GitOrigin-RevId: 478435764230e7fea3c8f26e5747562698f78228
Davinder Singh 2 лет назад
Родитель
Сommit
511ad1b68f

+ 3 - 0
services/web/app/src/Features/Project/ProjectListController.js

@@ -364,6 +364,7 @@ async function projectListPage(req, res, next) {
   )
 
   let showInrGeoBanner = false
+  let showBrlGeoBanner = false
   let recommendedCurrency
 
   if (usersBestSubscription?.type === 'free') {
@@ -372,6 +373,7 @@ async function projectListPage(req, res, next) {
     if (countryCode === 'IN') {
       showInrGeoBanner = true
     }
+    showBrlGeoBanner = countryCode === 'BR'
   }
 
   let hasIndividualRecurlySubscription = false
@@ -423,6 +425,7 @@ async function projectListPage(req, res, next) {
     showWritefullPromoBanner,
     recommendedCurrency,
     showInrGeoBanner,
+    showBrlGeoBanner,
     projectDashboardReact: true, // used in navbar
     groupSsoSetupSuccess,
     groupSubscriptionsPendingEnrollment:

+ 3 - 4
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -32,7 +32,6 @@ const validGroupPlanModalOptions = {
 
 async function plansPage(req, res) {
   const plans = SubscriptionViewModelBuilder.buildPlansList()
-
   let currency = null
   const queryCurrency = req.query.currency?.toUpperCase()
   if (GeoIpLookup.isValidCurrencyParam(queryCurrency)) {
@@ -66,7 +65,6 @@ async function plansPage(req, res) {
     currency: getDefault('currency', 'currency', defaultGroupPlanModalCurrency),
     usage: getDefault('usage', 'usage', 'enterprise'),
   }
-
   const plansPageViewSegmentation = {
     currency: recommendedCurrency,
     countryCode,
@@ -94,6 +92,7 @@ async function plansPage(req, res) {
     initialLocalizedGroupPrice:
       SubscriptionHelper.generateInitialLocalizedGroupPrice(currency),
     showInrGeoBanner: countryCode === 'IN',
+    showBrlGeoBanner: countryCode === 'BR',
   })
 }
 
@@ -210,7 +209,6 @@ async function interstitialPaymentPage(req, res) {
 
   const hasSubscription =
     await LimitationsManager.promises.userHasV1OrV2Subscription(user)
-
   const showSkipLink = req.query?.skipLink === 'true'
 
   if (hasSubscription) {
@@ -235,6 +233,7 @@ async function interstitialPaymentPage(req, res) {
       interstitialPaymentConfig,
       showSkipLink,
       showInrGeoBanner: countryCode === 'IN',
+      showBrlGeoBanner: countryCode === 'BR',
     })
   }
 }
@@ -552,7 +551,7 @@ async function _getRecommendedCurrency(req, res) {
   const countryCode = currencyLookup.countryCode
   let recommendedCurrency = currencyLookup.currencyCode
 
-  if (['BRL', 'MXN', 'COP', 'CLP', 'PEN'].includes(recommendedCurrency)) {
+  if (['MXN', 'COP', 'CLP', 'PEN'].includes(recommendedCurrency)) {
     recommendedCurrency = GeoIpLookup.DEFAULT_CURRENCY_CODE
   }
 

+ 1 - 0
services/web/app/views/project/list-react.pug

@@ -29,6 +29,7 @@ block append meta
 	meta(name="ol-showWritefullPromoBanner" data-type="boolean" content=showWritefullPromoBanner)
 	meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant)
 	meta(name="ol-showInrGeoBanner" data-type="boolean" content=showInrGeoBanner)
+	meta(name="ol-showBrlGeoBanner" data-type="boolean" content=showBrlGeoBanner)
 	meta(name="ol-recommendedCurrency" data-type="string" content=recommendedCurrency)
 	meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment)
 	meta(name="ol-hasIndividualRecurlySubscription" data-type="boolean" content=hasIndividualRecurlySubscription)

+ 3 - 0
services/web/app/views/subscriptions/interstitial-payment.pug

@@ -20,6 +20,9 @@ block content
 					if showInrGeoBanner
 						div.notification.notification-type-success.text-centered
 							div.notification-content !{translate("inr_discount_offer_plans_page_banner", {flag: '🇮🇳'})}
+					if showBrlGeoBanner
+						div.notification.notification-type-success.text-centered
+							div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
 
 					.row
 						.col-md-12

+ 3 - 0
services/web/app/views/subscriptions/plans.pug

@@ -18,6 +18,9 @@ block content
 					if showInrGeoBanner
 						div.notification.notification-type-success.text-centered
 							div.notification-content !{translate("inr_discount_offer_plans_page_banner", {flag: '🇮🇳'})}
+					if showBrlGeoBanner
+						div.notification.notification-type-success.text-centered
+							div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
 
 					.row
 						.col-md-12

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

@@ -104,6 +104,8 @@
   "binary_history_error": "",
   "blank_project": "",
   "blocked_filename": "",
+  "brl_discount_modal_info": "",
+  "brl_discount_modal_title": "",
   "browser": "",
   "bulk_accept_confirm": "",
   "bulk_reject_confirm": "",

+ 112 - 0
services/web/frontend/js/features/project-list/components/notifications/ads/brl-banner.tsx

@@ -0,0 +1,112 @@
+import { useCallback, useEffect, useRef, useState } from 'react'
+import usePersistedState from '../../../../../shared/hooks/use-persisted-state'
+import * as eventTracking from '../../../../../infrastructure/event-tracking'
+import { Modal, Button } from 'react-bootstrap'
+import AccessibleModal from '../../../../../shared/components/accessible-modal'
+import { useTranslation } from 'react-i18next'
+
+export default function BRLBanner() {
+  const { t } = useTranslation()
+  const [dismissedUntil, setDismissedUntil] = usePersistedState<
+    Date | undefined
+  >(`has_dismissed_brl_banner_until`)
+  const viewEventSent = useRef<boolean>(false)
+
+  const [showModal, setShowModal] = useState(true)
+
+  useEffect(() => {
+    if (dismissedUntil && new Date(dismissedUntil) > new Date()) {
+      return
+    }
+    if (!viewEventSent.current) {
+      eventTracking.sendMB('promo-prompt', {
+        location: 'dashboard-modal',
+        name: 'geo-pricing',
+        page: '/project',
+        content: 'modal',
+        country: 'BR',
+      })
+      viewEventSent.current = true
+    }
+  }, [dismissedUntil])
+
+  const handleClick = useCallback(() => {
+    eventTracking.sendMB('promo-click', {
+      location: 'dashboard-modal',
+      name: 'geo-pricing',
+      page: '/project',
+      content: 'modal',
+      country: 'BR',
+      type: 'click',
+    })
+
+    setShowModal(false)
+
+    window.open('/user/subscription/plans')
+  }, [])
+
+  const bannerDismissed = useCallback(() => {
+    eventTracking.sendMB('promo-dismiss', {
+      location: 'dashboard-modal',
+      name: 'geo-pricing',
+      page: '/project',
+      content: 'modal',
+      country: 'BR',
+    })
+    const until = new Date()
+    until.setDate(until.getDate() + 30) // 30 days
+    setDismissedUntil(until)
+  }, [setDismissedUntil])
+
+  const handleHide = useCallback(() => {
+    setShowModal(false)
+    bannerDismissed()
+  }, [bannerDismissed])
+
+  const handleMaybeLater = useCallback(() => {
+    eventTracking.sendMB('promo-click', {
+      location: 'dashboard-modal',
+      name: 'geo-pricing',
+      page: '/project',
+      content: 'modal',
+      country: 'BR',
+      type: 'pause',
+    })
+    setShowModal(false)
+    const until = new Date()
+    until.setDate(until.getDate() + 1) // 1 day
+    setDismissedUntil(until)
+  }, [setDismissedUntil])
+
+  if (dismissedUntil && new Date(dismissedUntil) > new Date()) {
+    return null
+  }
+
+  return (
+    <AccessibleModal show={showModal} onHide={handleHide}>
+      <Modal.Header closeButton>
+        <Modal.Title>{t('brl_discount_modal_title')}</Modal.Title>
+      </Modal.Header>
+      <Modal.Body className="modal-body-share">
+        <p>
+          <img
+            alt={t('brl_discount_modal_title')}
+            src="/img/subscriptions/blr-discount-modal.png"
+            style={{
+              width: '100%',
+            }}
+          />
+        </p>
+        <p>{t('brl_discount_modal_info')}</p>
+      </Modal.Body>
+      <Modal.Footer>
+        <Button bsStyle="default" onClick={handleMaybeLater}>
+          {t('maybe_later')}
+        </Button>
+        <Button type="button" bsStyle="primary" onClick={handleClick}>
+          {t('get_discounted_plan')}
+        </Button>
+      </Modal.Footer>
+    </AccessibleModal>
+  )
+}

+ 3 - 1
services/web/frontend/js/features/project-list/components/notifications/user-notifications.tsx

@@ -14,6 +14,7 @@ import customLocalStorage from '../../../../infrastructure/local-storage'
 import { sendMB } from '../../../../infrastructure/event-tracking'
 import classNames from 'classnames'
 import { isSplitTestEnabled } from '@/utils/splitTestUtils'
+import BRLBanner from './ads/brl-banner'
 
 const isChromium = () =>
   (window.navigator as any).userAgentData?.brands?.some(
@@ -42,8 +43,8 @@ function UserNotifications() {
     'ol-groupSubscriptionsPendingEnrollment',
     []
   )
-
   const showInrGeoBanner = getMeta('ol-showInrGeoBanner', false)
+  const showBrlGeoBanner = getMeta('ol-showBrlGeoBanner', false)
   const writefullIntegrationSplitTestEnabled = isSplitTestEnabled(
     'writefull-integration'
   )
@@ -111,6 +112,7 @@ function UserNotifications() {
         <ReconfirmationInfo />
         {!showWritefull && !dismissedWritefull && <GroupsAndEnterpriseBanner />}
         {showInrGeoBanner && <INRBanner />}
+        {showBrlGeoBanner && <BRLBanner />}
         {writefullBannerVariant === 'plans-page' ? (
           <WritefullPremiumPromoBanner
             show={showWritefull}

+ 3 - 0
services/web/locales/en.json

@@ -173,6 +173,9 @@
   "bonus_please_recommend_us": "Bonus - Please recommend us",
   "bonus_share_link_text": "Online LaTeX Editor __appName__",
   "bonus_twitter_share_text": "I’m using __appName__, the free online collaborative LaTeX editor - it’s awesome and easy to use!",
+  "brl_discount_modal_info": "Unlock the full potential of Overleaf with a 50% discount on premium subscriptions paid in Brazilian Reais. Get a longer compile timeout, full document history, track changes, additional collaborators, and more.",
+  "brl_discount_modal_title": "Premium subscription discount",
+  "brl_discount_offer_plans_page_banner": "__flag__ <b>Great news!</b> We’ve applied a 50% discount to premium plans on this page for our users in Brazil. Check out the new lower prices.",
   "browser": "Browser",
   "built_in": "Built-In",
   "bulk_accept_confirm": "Are you sure you want to accept the selected __nChanges__ changes?",

BIN
services/web/public/img/subscriptions/blr-discount-modal.png