mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 12:13:43 -05:00
Merge pull request #17559 from overleaf/ds-latam-v2
LATAM Geo Pricing - Introducing split test in LATAM countries Round 2 GitOrigin-RevId: 76ab880262f126f8db7f8e577154d78b5e9abf7a
This commit is contained in:
parent
d7d63e0f51
commit
998d55d159
16 changed files with 291 additions and 25 deletions
|
@ -359,15 +359,32 @@ async function projectListPage(req, res, next) {
|
||||||
|
|
||||||
let showInrGeoBanner = false
|
let showInrGeoBanner = false
|
||||||
let showBrlGeoBanner = false
|
let showBrlGeoBanner = false
|
||||||
|
let showLATAMBanner = false
|
||||||
let recommendedCurrency
|
let recommendedCurrency
|
||||||
|
|
||||||
if (usersBestSubscription?.type === 'free') {
|
if (usersBestSubscription?.type === 'free') {
|
||||||
const { countryCode } = await GeoIpLookup.promises.getCurrencyCode(req.ip)
|
const latamGeoPricingAssignment =
|
||||||
|
await SplitTestHandler.promises.getAssignment(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
'geo-pricing-latam-v2'
|
||||||
|
)
|
||||||
|
|
||||||
|
const { countryCode, currencyCode } =
|
||||||
|
await GeoIpLookup.promises.getCurrencyCode(req.ip)
|
||||||
|
|
||||||
if (countryCode === 'IN') {
|
if (countryCode === 'IN') {
|
||||||
showInrGeoBanner = true
|
showInrGeoBanner = true
|
||||||
}
|
}
|
||||||
showBrlGeoBanner = countryCode === 'BR'
|
showBrlGeoBanner = countryCode === 'BR'
|
||||||
|
|
||||||
|
showLATAMBanner =
|
||||||
|
latamGeoPricingAssignment.variant === 'latam' &&
|
||||||
|
['MX', 'CO', 'CL', 'PE'].includes(countryCode)
|
||||||
|
// LATAM Banner needs to know which currency to display
|
||||||
|
if (showLATAMBanner) {
|
||||||
|
recommendedCurrency = currencyCode
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hasIndividualRecurlySubscription = false
|
let hasIndividualRecurlySubscription = false
|
||||||
|
@ -426,6 +443,7 @@ async function projectListPage(req, res, next) {
|
||||||
showGroupsAndEnterpriseBanner,
|
showGroupsAndEnterpriseBanner,
|
||||||
groupsAndEnterpriseBannerVariant,
|
groupsAndEnterpriseBannerVariant,
|
||||||
showWritefullPromoBanner,
|
showWritefullPromoBanner,
|
||||||
|
showLATAMBanner,
|
||||||
recommendedCurrency,
|
recommendedCurrency,
|
||||||
showInrGeoBanner,
|
showInrGeoBanner,
|
||||||
showBrlGeoBanner,
|
showBrlGeoBanner,
|
||||||
|
|
|
@ -37,10 +37,10 @@ async function plansPage(req, res) {
|
||||||
if (GeoIpLookup.isValidCurrencyParam(queryCurrency)) {
|
if (GeoIpLookup.isValidCurrencyParam(queryCurrency)) {
|
||||||
currency = queryCurrency
|
currency = queryCurrency
|
||||||
}
|
}
|
||||||
const { recommendedCurrency, countryCode } = await _getRecommendedCurrency(
|
const { recommendedCurrency, countryCode, geoPricingLATAMTestVariant } =
|
||||||
req,
|
await _getRecommendedCurrency(req, res)
|
||||||
res
|
|
||||||
)
|
const latamCountryBannerDetails = await getLatamCountryBannerDetails(req, res)
|
||||||
if (recommendedCurrency && currency == null) {
|
if (recommendedCurrency && currency == null) {
|
||||||
currency = recommendedCurrency
|
currency = recommendedCurrency
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,7 @@ async function plansPage(req, res) {
|
||||||
const plansPageViewSegmentation = {
|
const plansPageViewSegmentation = {
|
||||||
currency: recommendedCurrency,
|
currency: recommendedCurrency,
|
||||||
countryCode,
|
countryCode,
|
||||||
|
'geo-pricing-latam-v2': geoPricingLATAMTestVariant,
|
||||||
}
|
}
|
||||||
|
|
||||||
AnalyticsManager.recordEventForSession(
|
AnalyticsManager.recordEventForSession(
|
||||||
|
@ -76,6 +77,10 @@ async function plansPage(req, res) {
|
||||||
plansPageViewSegmentation
|
plansPageViewSegmentation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const showLATAMBanner =
|
||||||
|
geoPricingLATAMTestVariant === 'latam' &&
|
||||||
|
['MX', 'CO', 'CL', 'PE'].includes(countryCode)
|
||||||
|
|
||||||
res.render('subscriptions/plans', {
|
res.render('subscriptions/plans', {
|
||||||
title: 'plans_and_pricing',
|
title: 'plans_and_pricing',
|
||||||
currentView,
|
currentView,
|
||||||
|
@ -93,6 +98,8 @@ async function plansPage(req, res) {
|
||||||
SubscriptionHelper.generateInitialLocalizedGroupPrice(currency),
|
SubscriptionHelper.generateInitialLocalizedGroupPrice(currency),
|
||||||
showInrGeoBanner: countryCode === 'IN',
|
showInrGeoBanner: countryCode === 'IN',
|
||||||
showBrlGeoBanner: countryCode === 'BR',
|
showBrlGeoBanner: countryCode === 'BR',
|
||||||
|
showLATAMBanner,
|
||||||
|
latamCountryBannerDetails,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,10 +209,10 @@ async function userSubscriptionPage(req, res) {
|
||||||
|
|
||||||
async function interstitialPaymentPage(req, res) {
|
async function interstitialPaymentPage(req, res) {
|
||||||
const user = SessionManager.getSessionUser(req.session)
|
const user = SessionManager.getSessionUser(req.session)
|
||||||
const { recommendedCurrency, countryCode } = await _getRecommendedCurrency(
|
const { recommendedCurrency, countryCode, geoPricingLATAMTestVariant } =
|
||||||
req,
|
await _getRecommendedCurrency(req, res)
|
||||||
res
|
|
||||||
)
|
const latamCountryBannerDetails = await getLatamCountryBannerDetails(req, res)
|
||||||
|
|
||||||
const hasSubscription =
|
const hasSubscription =
|
||||||
await LimitationsManager.promises.userHasV1OrV2Subscription(user)
|
await LimitationsManager.promises.userHasV1OrV2Subscription(user)
|
||||||
|
@ -217,6 +224,7 @@ async function interstitialPaymentPage(req, res) {
|
||||||
const paywallPlansPageViewSegmentation = {
|
const paywallPlansPageViewSegmentation = {
|
||||||
currency: recommendedCurrency,
|
currency: recommendedCurrency,
|
||||||
countryCode,
|
countryCode,
|
||||||
|
'geo-pricing-latam-v2': geoPricingLATAMTestVariant,
|
||||||
}
|
}
|
||||||
AnalyticsManager.recordEventForSession(
|
AnalyticsManager.recordEventForSession(
|
||||||
req.session,
|
req.session,
|
||||||
|
@ -224,6 +232,10 @@ async function interstitialPaymentPage(req, res) {
|
||||||
paywallPlansPageViewSegmentation
|
paywallPlansPageViewSegmentation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const showLATAMBanner =
|
||||||
|
geoPricingLATAMTestVariant === 'latam' &&
|
||||||
|
['MX', 'CO', 'CL', 'PE'].includes(countryCode)
|
||||||
|
|
||||||
res.render('subscriptions/interstitial-payment', {
|
res.render('subscriptions/interstitial-payment', {
|
||||||
title: 'subscribe',
|
title: 'subscribe',
|
||||||
itm_content: req.query?.itm_content,
|
itm_content: req.query?.itm_content,
|
||||||
|
@ -234,6 +246,8 @@ async function interstitialPaymentPage(req, res) {
|
||||||
showSkipLink,
|
showSkipLink,
|
||||||
showInrGeoBanner: countryCode === 'IN',
|
showInrGeoBanner: countryCode === 'IN',
|
||||||
showBrlGeoBanner: countryCode === 'BR',
|
showBrlGeoBanner: countryCode === 'BR',
|
||||||
|
showLATAMBanner,
|
||||||
|
latamCountryBannerDetails,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -551,16 +565,65 @@ async function _getRecommendedCurrency(req, res) {
|
||||||
const countryCode = currencyLookup.countryCode
|
const countryCode = currencyLookup.countryCode
|
||||||
let recommendedCurrency = currencyLookup.currencyCode
|
let recommendedCurrency = currencyLookup.currencyCode
|
||||||
|
|
||||||
if (['MXN', 'COP', 'CLP', 'PEN'].includes(recommendedCurrency)) {
|
const assignmentLATAM = await SplitTestHandler.promises.getAssignment(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
'geo-pricing-latam-v2'
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
['MXN', 'COP', 'CLP', 'PEN'].includes(recommendedCurrency) &&
|
||||||
|
assignmentLATAM?.variant === 'default'
|
||||||
|
) {
|
||||||
recommendedCurrency = GeoIpLookup.DEFAULT_CURRENCY_CODE
|
recommendedCurrency = GeoIpLookup.DEFAULT_CURRENCY_CODE
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recommendedCurrency,
|
recommendedCurrency,
|
||||||
countryCode,
|
countryCode,
|
||||||
|
geoPricingLATAMTestVariant: assignmentLATAM?.variant,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getLatamCountryBannerDetails(req, res) {
|
||||||
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||||
|
let ip = req.ip
|
||||||
|
if (
|
||||||
|
req.query?.ip &&
|
||||||
|
(await AuthorizationManager.promises.isUserSiteAdmin(userId))
|
||||||
|
) {
|
||||||
|
ip = req.query.ip
|
||||||
|
}
|
||||||
|
const currencyLookup = await GeoIpLookup.promises.getCurrencyCode(ip)
|
||||||
|
const countryCode = currencyLookup.countryCode
|
||||||
|
const latamCountryBannerDetails = {}
|
||||||
|
|
||||||
|
switch (countryCode) {
|
||||||
|
case `MX`:
|
||||||
|
latamCountryBannerDetails.latamCountryFlag = '🇲🇽'
|
||||||
|
latamCountryBannerDetails.country = 'Mexico'
|
||||||
|
latamCountryBannerDetails.discount = '25%'
|
||||||
|
break
|
||||||
|
case `CO`:
|
||||||
|
latamCountryBannerDetails.latamCountryFlag = '🇨🇴'
|
||||||
|
latamCountryBannerDetails.country = 'Colombia'
|
||||||
|
latamCountryBannerDetails.discount = '60%'
|
||||||
|
break
|
||||||
|
case `CL`:
|
||||||
|
latamCountryBannerDetails.latamCountryFlag = '🇨🇱'
|
||||||
|
latamCountryBannerDetails.country = 'Chile'
|
||||||
|
latamCountryBannerDetails.discount = '30%'
|
||||||
|
break
|
||||||
|
case `PE`:
|
||||||
|
latamCountryBannerDetails.latamCountryFlag = '🇵🇪'
|
||||||
|
latamCountryBannerDetails.country = 'Peru'
|
||||||
|
latamCountryBannerDetails.discount = '40%'
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
return latamCountryBannerDetails
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
plansPage: expressify(plansPage),
|
plansPage: expressify(plansPage),
|
||||||
userSubscriptionPage: expressify(userSubscriptionPage),
|
userSubscriptionPage: expressify(userSubscriptionPage),
|
||||||
|
|
|
@ -31,6 +31,7 @@ block append meta
|
||||||
meta(name="ol-showInrGeoBanner" data-type="boolean" content=showInrGeoBanner)
|
meta(name="ol-showInrGeoBanner" data-type="boolean" content=showInrGeoBanner)
|
||||||
meta(name="ol-showBrlGeoBanner" data-type="boolean" content=showBrlGeoBanner)
|
meta(name="ol-showBrlGeoBanner" data-type="boolean" content=showBrlGeoBanner)
|
||||||
meta(name="ol-recommendedCurrency" data-type="string" content=recommendedCurrency)
|
meta(name="ol-recommendedCurrency" data-type="string" content=recommendedCurrency)
|
||||||
|
meta(name="ol-showLATAMBanner" data-type="boolean" content=showLATAMBanner)
|
||||||
meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment)
|
meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment)
|
||||||
meta(name="ol-hasIndividualRecurlySubscription" data-type="boolean" content=hasIndividualRecurlySubscription)
|
meta(name="ol-hasIndividualRecurlySubscription" data-type="boolean" content=hasIndividualRecurlySubscription)
|
||||||
meta(name="ol-newNotificationStyle" data-type="boolean" content=newNotificationStyle)
|
meta(name="ol-newNotificationStyle" data-type="boolean" content=newNotificationStyle)
|
||||||
|
|
|
@ -23,6 +23,9 @@ block content
|
||||||
if showBrlGeoBanner
|
if showBrlGeoBanner
|
||||||
div.notification.notification-type-success.text-centered
|
div.notification.notification-type-success.text-centered
|
||||||
div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
|
div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
|
||||||
|
if showLATAMBanner
|
||||||
|
div.notification.notification-type-success.text-centered
|
||||||
|
div.notification-content !{translate("latam_discount_offer_plans_page_banner", {flag: latamCountryBannerDetails.latamCountryFlag, country: latamCountryBannerDetails.country, discount: latamCountryBannerDetails.discount })}
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col-md-12
|
.col-md-12
|
||||||
|
|
|
@ -21,6 +21,9 @@ block content
|
||||||
if showBrlGeoBanner
|
if showBrlGeoBanner
|
||||||
div.notification.notification-type-success.text-centered
|
div.notification.notification-type-success.text-centered
|
||||||
div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
|
div.notification-content !{translate("brl_discount_offer_plans_page_banner", {flag: '🇧🇷'})}
|
||||||
|
if showLATAMBanner
|
||||||
|
div.notification.notification-type-success.text-centered
|
||||||
|
div.notification-content !{translate("latam_discount_offer_plans_page_banner", {flag: latamCountryBannerDetails.latamCountryFlag, country: latamCountryBannerDetails.country, discount: latamCountryBannerDetails.discount })}
|
||||||
|
|
||||||
.row
|
.row
|
||||||
.col-md-12
|
.col-md-12
|
||||||
|
|
|
@ -107,8 +107,6 @@
|
||||||
"binary_history_error": "",
|
"binary_history_error": "",
|
||||||
"blank_project": "",
|
"blank_project": "",
|
||||||
"blocked_filename": "",
|
"blocked_filename": "",
|
||||||
"brl_discount_modal_info": "",
|
|
||||||
"brl_discount_modal_title": "",
|
|
||||||
"browser": "",
|
"browser": "",
|
||||||
"bulk_accept_confirm": "",
|
"bulk_accept_confirm": "",
|
||||||
"bulk_reject_confirm": "",
|
"bulk_reject_confirm": "",
|
||||||
|
@ -655,6 +653,8 @@
|
||||||
"last_resort_trouble_shooting_guide": "",
|
"last_resort_trouble_shooting_guide": "",
|
||||||
"last_updated_date_by_x": "",
|
"last_updated_date_by_x": "",
|
||||||
"last_used": "",
|
"last_used": "",
|
||||||
|
"latam_discount_modal_info": "",
|
||||||
|
"latam_discount_modal_title": "",
|
||||||
"latex_places_figures_according_to_a_special_algorithm": "",
|
"latex_places_figures_according_to_a_special_algorithm": "",
|
||||||
"latex_places_tables_according_to_a_special_algorithm": "",
|
"latex_places_tables_according_to_a_special_algorithm": "",
|
||||||
"layout": "",
|
"layout": "",
|
||||||
|
|
|
@ -85,19 +85,24 @@ export default function BRLBanner() {
|
||||||
return (
|
return (
|
||||||
<AccessibleModal show={showModal} onHide={handleHide}>
|
<AccessibleModal show={showModal} onHide={handleHide}>
|
||||||
<Modal.Header closeButton>
|
<Modal.Header closeButton>
|
||||||
<Modal.Title>{t('brl_discount_modal_title')}</Modal.Title>
|
<Modal.Title>{t('latam_discount_modal_title')}</Modal.Title>
|
||||||
</Modal.Header>
|
</Modal.Header>
|
||||||
<Modal.Body className="modal-body-share">
|
<Modal.Body className="modal-body-share">
|
||||||
<p>
|
<p>
|
||||||
<img
|
<img
|
||||||
alt={t('brl_discount_modal_title')}
|
alt={t('latam_discount_modal_title')}
|
||||||
src="/img/subscriptions/blr-discount-modal.png"
|
src="/img/subscriptions/blr-discount-modal.png"
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
<p>{t('brl_discount_modal_info')}</p>
|
<p>
|
||||||
|
{t('latam_discount_modal_info', {
|
||||||
|
discount: '50%',
|
||||||
|
currencyName: 'Brazilian Reais',
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
<Modal.Footer>
|
<Modal.Footer>
|
||||||
<Button bsStyle="default" onClick={handleMaybeLater}>
|
<Button bsStyle="default" onClick={handleMaybeLater}>
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
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'
|
||||||
|
import getMeta from '@/utils/meta'
|
||||||
|
|
||||||
|
const LATAM_CURRENCIES = {
|
||||||
|
MXN: {
|
||||||
|
name: 'Mexican Pesos',
|
||||||
|
countryCode: 'MX',
|
||||||
|
discountCode: '25',
|
||||||
|
imageSource: '/img/subscriptions/mexico-discount-modal.png',
|
||||||
|
},
|
||||||
|
COP: {
|
||||||
|
name: 'Colombian Pesos',
|
||||||
|
countryCode: 'CO',
|
||||||
|
discountCode: '60',
|
||||||
|
imageSource: '/img/subscriptions/colombia-discount-modal.png',
|
||||||
|
},
|
||||||
|
CLP: {
|
||||||
|
name: 'Chilean Pesos',
|
||||||
|
countryCode: 'CL',
|
||||||
|
discountCode: '30',
|
||||||
|
imageSource: '/img/subscriptions/chile-discount-modal.png',
|
||||||
|
},
|
||||||
|
PEN: {
|
||||||
|
name: 'Peruvian Soles',
|
||||||
|
countryCode: 'PE',
|
||||||
|
discountCode: '40',
|
||||||
|
imageSource: '/img/subscriptions/peru-discount-modal.png',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function LATAMBanner() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [dismissedUntil, setDismissedUntil] = usePersistedState<
|
||||||
|
Date | undefined
|
||||||
|
>(`has_dismissed_latam_banner_until`)
|
||||||
|
const viewEventSent = useRef<boolean>(false)
|
||||||
|
const [showModal, setShowModal] = useState(true)
|
||||||
|
|
||||||
|
const currency = getMeta('ol-recommendedCurrency')
|
||||||
|
const {
|
||||||
|
imageSource,
|
||||||
|
name: currencyName,
|
||||||
|
discountCode,
|
||||||
|
countryCode,
|
||||||
|
} = LATAM_CURRENCIES[currency as keyof typeof LATAM_CURRENCIES]
|
||||||
|
|
||||||
|
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: countryCode,
|
||||||
|
})
|
||||||
|
viewEventSent.current = true
|
||||||
|
}
|
||||||
|
}, [dismissedUntil, countryCode])
|
||||||
|
|
||||||
|
const handleClick = useCallback(() => {
|
||||||
|
eventTracking.sendMB('promo-click', {
|
||||||
|
location: 'dashboard-modal',
|
||||||
|
name: 'geo-pricing',
|
||||||
|
page: '/project',
|
||||||
|
content: 'modal',
|
||||||
|
country: countryCode,
|
||||||
|
type: 'click',
|
||||||
|
})
|
||||||
|
|
||||||
|
setShowModal(false)
|
||||||
|
|
||||||
|
window.open('/user/subscription/plans')
|
||||||
|
}, [countryCode])
|
||||||
|
|
||||||
|
const bannerDismissed = useCallback(() => {
|
||||||
|
eventTracking.sendMB('promo-dismiss', {
|
||||||
|
location: 'dashboard-modal',
|
||||||
|
name: 'geo-pricing',
|
||||||
|
page: '/project',
|
||||||
|
content: 'modal',
|
||||||
|
country: countryCode,
|
||||||
|
})
|
||||||
|
const until = new Date()
|
||||||
|
until.setDate(until.getDate() + 30) // 30 days
|
||||||
|
setDismissedUntil(until)
|
||||||
|
}, [setDismissedUntil, countryCode])
|
||||||
|
|
||||||
|
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: countryCode,
|
||||||
|
type: 'pause',
|
||||||
|
})
|
||||||
|
setShowModal(false)
|
||||||
|
const until = new Date()
|
||||||
|
until.setDate(until.getDate() + 1) // 1 day
|
||||||
|
setDismissedUntil(until)
|
||||||
|
}, [setDismissedUntil, countryCode])
|
||||||
|
|
||||||
|
if (dismissedUntil && new Date(dismissedUntil) > new Date()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Safety, but should always be a valid LATAM currency if ol-showLATAMBanner is true
|
||||||
|
if (!(currency in LATAM_CURRENCIES)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AccessibleModal show={showModal} onHide={handleHide} backdrop="static">
|
||||||
|
<Modal.Header closeButton>
|
||||||
|
<Modal.Title>{t('latam_discount_modal_title')}</Modal.Title>
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Body className="modal-body-share">
|
||||||
|
<p>
|
||||||
|
<img
|
||||||
|
alt={t('latam_discount_modal_title')}
|
||||||
|
src={imageSource}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{t('latam_discount_modal_info', {
|
||||||
|
discount: discountCode,
|
||||||
|
currencyName,
|
||||||
|
})}
|
||||||
|
</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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import getMeta from '@/utils/meta'
|
||||||
|
import BRLBanner from './ads/brl-banner'
|
||||||
|
import INRBanner from './ads/inr-banner'
|
||||||
|
import LATAMBanner from './ads/latam-banner'
|
||||||
|
|
||||||
|
function GeoBanners() {
|
||||||
|
const showInrGeoBanner = getMeta('ol-showInrGeoBanner', false)
|
||||||
|
const showBrlGeoBanner = getMeta('ol-showBrlGeoBanner', false)
|
||||||
|
const showLATAMBanner = getMeta('ol-showLATAMBanner', false)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showBrlGeoBanner && <BRLBanner />}
|
||||||
|
{showLATAMBanner && <LATAMBanner />}
|
||||||
|
{showInrGeoBanner && <INRBanner />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GeoBanners
|
|
@ -6,13 +6,12 @@ import ReconfirmationInfo from './groups/affiliation/reconfirmation-info'
|
||||||
import GroupsAndEnterpriseBanner from './groups-and-enterprise-banner'
|
import GroupsAndEnterpriseBanner from './groups-and-enterprise-banner'
|
||||||
import WritefullPremiumPromoBanner from './writefull-premium-promo-banner'
|
import WritefullPremiumPromoBanner from './writefull-premium-promo-banner'
|
||||||
import GroupSsoSetupSuccess from './groups/group-sso-setup-success'
|
import GroupSsoSetupSuccess from './groups/group-sso-setup-success'
|
||||||
import INRBanner from './ads/inr-banner'
|
|
||||||
import getMeta from '../../../../utils/meta'
|
import getMeta from '../../../../utils/meta'
|
||||||
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
||||||
import customLocalStorage from '../../../../infrastructure/local-storage'
|
import customLocalStorage from '../../../../infrastructure/local-storage'
|
||||||
import { sendMB } from '../../../../infrastructure/event-tracking'
|
import { sendMB } from '../../../../infrastructure/event-tracking'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import BRLBanner from './ads/brl-banner'
|
import GeoBanners from './geo-banners'
|
||||||
|
|
||||||
type Subscription = {
|
type Subscription = {
|
||||||
groupId: string
|
groupId: string
|
||||||
|
@ -36,8 +35,6 @@ function UserNotifications() {
|
||||||
'ol-groupSubscriptionsPendingEnrollment',
|
'ol-groupSubscriptionsPendingEnrollment',
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
const showInrGeoBanner = getMeta('ol-showInrGeoBanner', false)
|
|
||||||
const showBrlGeoBanner = getMeta('ol-showBrlGeoBanner', false)
|
|
||||||
const user = getMeta('ol-user')
|
const user = getMeta('ol-user')
|
||||||
|
|
||||||
// Temporary workaround to prevent also showing groups/enterprise banner
|
// Temporary workaround to prevent also showing groups/enterprise banner
|
||||||
|
@ -85,8 +82,8 @@ function UserNotifications() {
|
||||||
<Institution />
|
<Institution />
|
||||||
<ConfirmEmail />
|
<ConfirmEmail />
|
||||||
<ReconfirmationInfo />
|
<ReconfirmationInfo />
|
||||||
|
<GeoBanners />
|
||||||
{!showWritefull && !dismissedWritefull && <GroupsAndEnterpriseBanner />}
|
{!showWritefull && !dismissedWritefull && <GroupsAndEnterpriseBanner />}
|
||||||
{showInrGeoBanner && <INRBanner />}
|
|
||||||
|
|
||||||
<WritefullPremiumPromoBanner
|
<WritefullPremiumPromoBanner
|
||||||
show={showWritefull}
|
show={showWritefull}
|
||||||
|
@ -95,7 +92,6 @@ function UserNotifications() {
|
||||||
setDismissedWritefull(true)
|
setDismissedWritefull(true)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{showBrlGeoBanner && <BRLBanner />}
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -176,8 +176,6 @@
|
||||||
"bonus_please_recommend_us": "Bonus - Please recommend us",
|
"bonus_please_recommend_us": "Bonus - Please recommend us",
|
||||||
"bonus_share_link_text": "Online LaTeX Editor __appName__",
|
"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!",
|
"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.",
|
"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",
|
"browser": "Browser",
|
||||||
"built_in": "Built-In",
|
"built_in": "Built-In",
|
||||||
|
@ -972,6 +970,9 @@
|
||||||
"last_updated": "Last Updated",
|
"last_updated": "Last Updated",
|
||||||
"last_updated_date_by_x": "__lastUpdatedDate__ by __person__",
|
"last_updated_date_by_x": "__lastUpdatedDate__ by __person__",
|
||||||
"last_used": "last used",
|
"last_used": "last used",
|
||||||
|
"latam_discount_modal_info": "Unlock the full potential of Overleaf with a __discount__% discount on premium subscriptions paid in __currencyName__. Get a longer compile timeout, full document history, track changes, additional collaborators, and more.",
|
||||||
|
"latam_discount_modal_title": "Premium subscription discount",
|
||||||
|
"latam_discount_offer_plans_page_banner": "__flag__ <b>Great news!</b> We’ve applied a __discount__ discount to premium plans on this page for our users in __country__. Check out the new lower prices.",
|
||||||
"latex_articles_page_summary": "Papers, presentations, reports and more, written in LaTeX and published by our community. Search or browse below.",
|
"latex_articles_page_summary": "Papers, presentations, reports and more, written in LaTeX and published by our community. Search or browse below.",
|
||||||
"latex_articles_page_title": "Articles - Papers, Presentations, Reports and more",
|
"latex_articles_page_title": "Articles - Papers, Presentations, Reports and more",
|
||||||
"latex_editor_info": "Everything you need in a modern LaTeX editor --- spell check, intelligent autocomplete, syntax highlighting, dozens of color themes, vim and emacs bindings, help with LaTeX warnings and error messages, and much more.",
|
"latex_editor_info": "Everything you need in a modern LaTeX editor --- spell check, intelligent autocomplete, syntax highlighting, dozens of color themes, vim and emacs bindings, help with LaTeX warnings and error messages, and much more.",
|
||||||
|
|
|
@ -85,8 +85,6 @@
|
||||||
"bonus_please_recommend_us": "Bônus - Por favor nos recomende",
|
"bonus_please_recommend_us": "Bônus - Por favor nos recomende",
|
||||||
"bonus_share_link_text": "Editor Online de LaTeX __appName__",
|
"bonus_share_link_text": "Editor Online de LaTeX __appName__",
|
||||||
"bonus_twitter_share_text": "Estou usando o __appName__, o editor gratuito online e colaborativo de LaTeX - é muito bom e fácil de usar!",
|
"bonus_twitter_share_text": "Estou usando o __appName__, o editor gratuito online e colaborativo de LaTeX - é muito bom e fácil de usar!",
|
||||||
"brl_discount_modal_info": "Obtenha todo o potencial do Overleaf com desconto de 50% em assinaturas premium pagas em reais. Obtenha um tempo limite de compilação mais longo, histórico completo de documentos, controle de alterações, colaboradores adicionais e muito mais.",
|
|
||||||
"brl_discount_modal_title": "Desconto em assinaturas premium",
|
|
||||||
"brl_discount_offer_plans_page_banner": "__flag__ Aplicamos um desconto de 50% aos planos premium nesta página para nossos usuários no Brasil. Confira os novos preços mais baixos.",
|
"brl_discount_offer_plans_page_banner": "__flag__ Aplicamos um desconto de 50% aos planos premium nesta página para nossos usuários no Brasil. Confira os novos preços mais baixos.",
|
||||||
"built_in": "Embutido",
|
"built_in": "Embutido",
|
||||||
"bulk_accept_confirm": "Vocês tem certeza que deseja aceitar as __nChanges__ alterações selecionadas?",
|
"bulk_accept_confirm": "Vocês tem certeza que deseja aceitar as __nChanges__ alterações selecionadas?",
|
||||||
|
@ -327,6 +325,8 @@
|
||||||
"language": "Idioma",
|
"language": "Idioma",
|
||||||
"last_modified": "Última Modificação",
|
"last_modified": "Última Modificação",
|
||||||
"last_name": "Sobrenome",
|
"last_name": "Sobrenome",
|
||||||
|
"latam_discount_modal_info": "Obtenha todo o potencial do Overleaf com desconto de __discount__% em assinaturas premium pagas em __currencyName__. Obtenha um tempo limite de compilação mais longo, histórico completo de documentos, controle de alterações, colaboradores adicionais e muito mais.",
|
||||||
|
"latam_discount_modal_title": "Desconto em assinaturas premium",
|
||||||
"latex_editor_info": "Tudo que você precisa num editar moderno de LaTeX - correção ortográfica, auto-completar inteligente, realce de sintaxe, diversas cores de temas, atalhos de vim e emacs, ajuda com mensagens de aviso e erros do LaTeX e muito mais.",
|
"latex_editor_info": "Tudo que você precisa num editar moderno de LaTeX - correção ortográfica, auto-completar inteligente, realce de sintaxe, diversas cores de temas, atalhos de vim e emacs, ajuda com mensagens de aviso e erros do LaTeX e muito mais.",
|
||||||
"latex_templates": "Modelos LaTeX",
|
"latex_templates": "Modelos LaTeX",
|
||||||
"ldap": "LDAP",
|
"ldap": "LDAP",
|
||||||
|
|
BIN
services/web/public/img/subscriptions/chile-discount-modal.png
Normal file
BIN
services/web/public/img/subscriptions/chile-discount-modal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
BIN
services/web/public/img/subscriptions/mexico-discount-modal.png
Normal file
BIN
services/web/public/img/subscriptions/mexico-discount-modal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
BIN
services/web/public/img/subscriptions/peru-discount-modal.png
Normal file
BIN
services/web/public/img/subscriptions/peru-discount-modal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
Loading…
Reference in a new issue