mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Implement Back to School 2023 banners and modal (#14363)
* Implement Back to School 2023 banners and modal * Only show WritefullPromoBanner if not showing BackToSchoolModal GitOrigin-RevId: 3bd2ea48fa6d66f112cc26662a61be05cf7daafd
This commit is contained in:
parent
3f7b84cb40
commit
43c92642c6
15 changed files with 311 additions and 3 deletions
|
@ -452,6 +452,7 @@ async function projectListPage(req, res, next) {
|
|||
showInrGeoBanner,
|
||||
inrGeoBannerVariant,
|
||||
inrGeoBannerSplitTestName,
|
||||
showBackToSchoolModal: Boolean(usersBestSubscription?.type === 'free'),
|
||||
projectDashboardReact: true, // used in navbar
|
||||
welcomePageRedesignVariant: welcomePageRedesignAssignment.variant,
|
||||
groupSubscriptionsPendingEnrollment:
|
||||
|
|
|
@ -130,6 +130,16 @@ async function plansPage(req, res) {
|
|||
plansPageViewSegmentation[inrGeoBannerSplitTestName] = inrGeoBannerVariant
|
||||
}
|
||||
|
||||
let showBackToSchoolBanner
|
||||
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||
if (userId) {
|
||||
const usersBestSubscription =
|
||||
await SubscriptionViewModelBuilder.promises.getBestSubscription({
|
||||
_id: userId,
|
||||
})
|
||||
showBackToSchoolBanner = usersBestSubscription?.type === 'free'
|
||||
}
|
||||
|
||||
AnalyticsManager.recordEventForSession(
|
||||
req.session,
|
||||
'plans-page-view',
|
||||
|
@ -152,6 +162,7 @@ async function plansPage(req, res) {
|
|||
initialLocalizedGroupPrice:
|
||||
SubscriptionHelper.generateInitialLocalizedGroupPrice(currency),
|
||||
showInrGeoBanner,
|
||||
showBackToSchoolBanner,
|
||||
annualTrialsAssignment: annualTrialsAssignment?.variant,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ block append meta
|
|||
meta(name="ol-inrGeoBannerSplitTestName" data-type="string" content=inrGeoBannerSplitTestName)
|
||||
meta(name="ol-showLATAMBanner" data-type="boolean" content=showLATAMBanner)
|
||||
meta(name="ol-recommendedCurrency" data-type="string" content=recommendedCurrency)
|
||||
meta(name="ol-showBackToSchoolModal" data-type="boolean" content=showBackToSchoolModal)
|
||||
meta(name="ol-welcomePageRedesignVariant" data-type="string" content=welcomePageRedesignVariant)
|
||||
meta(name="ol-groupSubscriptionsPendingEnrollment" data-type="json" content=groupSubscriptionsPendingEnrollment)
|
||||
|
||||
|
|
|
@ -14,6 +14,31 @@ block append meta
|
|||
|
||||
block content
|
||||
main.content.content-alt#main-content
|
||||
.container
|
||||
.user-notifications
|
||||
ul.list-unstyled(ng-cloak)
|
||||
li.notification-entry
|
||||
div.alert.alert-back-to-school(
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-prompt"
|
||||
event-tracking-trigger="load"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner"}'
|
||||
)
|
||||
.notification-body
|
||||
| 🎉
|
||||
p
|
||||
strong #{translate("back_to_school_banner_bargain_with_x_percent_off_in_school_or_not", {x: '15'})}
|
||||
br
|
||||
| #{translate("back_to_school_banner_hurry_offer_ends_sep_30")}
|
||||
.notification-action
|
||||
a.btn.btn-sm.btn-default-outline(
|
||||
href="/about/back-to-school-promo-2023"
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-click"
|
||||
event-tracking-trigger="click"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner", "type": "click"}'
|
||||
) #{translate('claim_discounts')}
|
||||
|
||||
.content-page
|
||||
.plans
|
||||
.container
|
||||
|
|
|
@ -14,6 +14,31 @@ block append meta
|
|||
|
||||
block content
|
||||
main.content.content-alt#main-content
|
||||
.container
|
||||
.user-notifications
|
||||
ul.list-unstyled(ng-cloak)
|
||||
li.notification-entry
|
||||
div.alert.alert-back-to-school(
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-prompt"
|
||||
event-tracking-trigger="load"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner"}'
|
||||
)
|
||||
.notification-body
|
||||
| 🎉
|
||||
p
|
||||
strong #{translate("back_to_school_banner_bargain_with_x_percent_off_in_school_or_not", {x: '15'})}
|
||||
br
|
||||
| #{translate("back_to_school_banner_hurry_offer_ends_sep_30")}
|
||||
.notification-action
|
||||
a.btn.btn-sm.btn-default-outline(
|
||||
href="/about/back-to-school-promo-2023"
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-click"
|
||||
event-tracking-trigger="click"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner", "type": "click"}'
|
||||
) #{translate('claim_discounts')}
|
||||
|
||||
.content-page
|
||||
.plans
|
||||
.container
|
||||
|
|
|
@ -14,6 +14,31 @@ block append meta
|
|||
|
||||
block content
|
||||
main.content.content-alt#main-content
|
||||
.container
|
||||
.user-notifications
|
||||
ul.list-unstyled(ng-cloak)
|
||||
li.notification-entry
|
||||
div.alert.alert-back-to-school(
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-prompt"
|
||||
event-tracking-trigger="load"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner"}'
|
||||
)
|
||||
.notification-body
|
||||
| 🎉
|
||||
p
|
||||
strong #{translate("back_to_school_banner_bargain_with_x_percent_off_in_school_or_not", {x: '15'})}
|
||||
br
|
||||
| #{translate("back_to_school_banner_hurry_offer_ends_sep_30")}
|
||||
.notification-action
|
||||
a.btn.btn-sm.btn-default-outline(
|
||||
href="/about/back-to-school-promo-2023"
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-click"
|
||||
event-tracking-trigger="click"
|
||||
event-segmentation='{"location": "interstitial-page-banner", "name": "bts2023", "content": "banner", "type": "click"}'
|
||||
) #{translate('claim_discounts')}
|
||||
|
||||
.content-page
|
||||
.plans
|
||||
.container
|
||||
|
|
|
@ -12,12 +12,40 @@ block append meta
|
|||
|
||||
block content
|
||||
main.content.content-alt#main-content
|
||||
if showBackToSchoolBanner
|
||||
.container(
|
||||
)
|
||||
.user-notifications
|
||||
ul.list-unstyled(ng-cloak)
|
||||
li.notification-entry
|
||||
div.alert.alert-back-to-school(
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-prompt"
|
||||
event-tracking-trigger="load"
|
||||
event-segmentation='{"location": "plans-page-banner", "name": "bts2023", "content": "banner"}'
|
||||
)
|
||||
.notification-body
|
||||
| 🎉
|
||||
p
|
||||
strong #{translate("back_to_school_banner_bargain_with_x_percent_off_in_school_or_not", {x: '15'})}
|
||||
br
|
||||
| #{translate("back_to_school_banner_hurry_offer_ends_sep_30")}
|
||||
.notification-action
|
||||
a.btn.btn-sm.btn-default-outline(
|
||||
href="/about/back-to-school-promo-2023"
|
||||
event-tracking-mb="true"
|
||||
event-tracking="promo-click"
|
||||
event-tracking-trigger="click"
|
||||
event-segmentation='{"location": "plans-page-banner", "name": "bts2023", "content": "banner", "type": "click"}'
|
||||
) #{translate('claim_discounts')}
|
||||
|
||||
.content-page
|
||||
.plans
|
||||
.container(ng-cloak)
|
||||
if showInrGeoBanner
|
||||
div.alert.alert-success.text-centered !{translate("inr_discount_offer_plans_page_banner", {flag: '🇮🇳'})}
|
||||
|
||||
|
||||
.row
|
||||
.col-md-12
|
||||
.page-header.centered.plans-header.text-centered.top-page-header
|
||||
|
|
|
@ -86,6 +86,9 @@
|
|||
"autocomplete": "",
|
||||
"autocomplete_references": "",
|
||||
"back": "",
|
||||
"back_to_school_modal_bargain_with_x_percent_off": "",
|
||||
"back_to_school_modal_offer_ends_sep_30": "",
|
||||
"back_to_school_modal_offers_from_writefull_and_papers": "",
|
||||
"back_to_subscription": "",
|
||||
"back_to_your_projects": "",
|
||||
"beta_program_already_participating": "",
|
||||
|
@ -139,6 +142,7 @@
|
|||
"checking_project_github_status": "",
|
||||
"choose_a_custom_color": "",
|
||||
"choose_from_group_members": "",
|
||||
"claim_discounts": "",
|
||||
"clear_cached_files": "",
|
||||
"clear_search": "",
|
||||
"click_here_to_view_sl_in_lng": "",
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
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'
|
||||
|
||||
export default function BackToSchoolModal() {
|
||||
const { t } = useTranslation()
|
||||
const [dismissedUntil, setDismissedUntil] = usePersistedState<
|
||||
Date | undefined
|
||||
>(`has_dismissed_back_to_school_modal_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', {
|
||||
name: 'bts2023',
|
||||
location: 'dashboard-modal',
|
||||
content: 'modal',
|
||||
})
|
||||
viewEventSent.current = true
|
||||
}
|
||||
}, [dismissedUntil])
|
||||
|
||||
const handleClick = useCallback(() => {
|
||||
eventTracking.sendMB('promo-click', {
|
||||
name: 'bts2023',
|
||||
location: 'dashboard-modal',
|
||||
content: 'modal',
|
||||
type: 'click',
|
||||
})
|
||||
|
||||
setShowModal(false)
|
||||
|
||||
window.open('/about/back-to-school-promo-2023')
|
||||
}, [])
|
||||
|
||||
const bannerDismissed = useCallback(() => {
|
||||
eventTracking.sendMB('promo-dismiss', {
|
||||
name: 'bts2023',
|
||||
location: 'dashboard-modal',
|
||||
content: 'modal',
|
||||
})
|
||||
const until = new Date()
|
||||
until.setDate(until.getDate() + 14) // 14 days
|
||||
setDismissedUntil(until)
|
||||
}, [setDismissedUntil])
|
||||
|
||||
const handleHide = useCallback(() => {
|
||||
setShowModal(false)
|
||||
bannerDismissed()
|
||||
}, [bannerDismissed])
|
||||
|
||||
const handleMaybeLater = useCallback(() => {
|
||||
eventTracking.sendMB('promo-click', {
|
||||
name: 'bts2023',
|
||||
location: 'dashboard-modal',
|
||||
content: 'modal',
|
||||
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('back_to_school_modal_bargain_with_x_percent_off', { x: '15' })}
|
||||
</Modal.Title>
|
||||
</Modal.Header>
|
||||
<Modal.Body className="modal-body-share">
|
||||
<p>
|
||||
<img
|
||||
alt=""
|
||||
src="/img/subscriptions/back-to-school-modal.png"
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>{t('back_to_school_modal_offers_from_writefull_and_papers')}</p>
|
||||
<p>
|
||||
<strong>{t('back_to_school_modal_offer_ends_sep_30')}</strong>
|
||||
</p>
|
||||
</Modal.Body>
|
||||
<Modal.Footer>
|
||||
<Button bsStyle="default" onClick={handleMaybeLater}>
|
||||
{t('maybe_later')}
|
||||
</Button>
|
||||
<Button type="button" bsStyle="primary" onClick={handleClick}>
|
||||
{t('claim_discounts')}
|
||||
</Button>
|
||||
</Modal.Footer>
|
||||
</AccessibleModal>
|
||||
)
|
||||
}
|
|
@ -9,6 +9,7 @@ import INRBanner from './ads/inr-banner'
|
|||
import LATAMBanner from './ads/latam-banner'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
|
||||
import BackToSchoolModal from './ads/back-to-school-modal'
|
||||
|
||||
type Subscription = {
|
||||
groupId: string
|
||||
|
@ -28,8 +29,12 @@ function UserNotifications() {
|
|||
'ol-groupSubscriptionsPendingEnrollment',
|
||||
[]
|
||||
)
|
||||
const showBackToSchoolModal = getMeta('ol-showBackToSchoolModal', false)
|
||||
|
||||
const showInrGeoBanner = getMeta('ol-showInrGeoBanner', false)
|
||||
const inrGeoBannerVariant = getMeta('ol-inrGeoBannerVariant', 'default')
|
||||
const inrGeoBannerVariant = showBackToSchoolModal
|
||||
? 'default' // This test should be disabled to prevent double modals, but sanity check to be safe
|
||||
: getMeta('ol-inrGeoBannerVariant', 'default')
|
||||
const inrGeoBannerSplitTestName = getMeta(
|
||||
'ol-inrGeoBannerSplitTestName',
|
||||
'unassigned'
|
||||
|
@ -61,7 +66,11 @@ function UserNotifications() {
|
|||
) : (
|
||||
<GroupsAndEnterpriseBanner />
|
||||
)}
|
||||
<WritefullPromoBanner />
|
||||
{showBackToSchoolModal ? (
|
||||
<BackToSchoolModal />
|
||||
) : (
|
||||
<WritefullPromoBanner />
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
position: relative;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
padding-top: @header-height;
|
||||
padding-top: 0px; // temporarily change from @header-height to 0px for back to school banner changes
|
||||
h1,
|
||||
p,
|
||||
label {
|
||||
|
@ -331,6 +331,41 @@
|
|||
box-shadow: 0 3px 5px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.back-to-school {
|
||||
.back-to-school-banner {
|
||||
background: @blue-10;
|
||||
padding: 0px 16px 0px 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid @blue-20;
|
||||
color: @neutral-90;
|
||||
.banner-row {
|
||||
height: 56px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.claim-my-discount {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-left: 10px;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
.navbar-default {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: @screen-sm-max) {
|
||||
.doc-history-example {
|
||||
margin-bottom: @margin-md;
|
||||
|
|
|
@ -84,6 +84,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Back to school promo
|
||||
*/
|
||||
|
||||
.alert-back-to-school {
|
||||
.alert-variant(@green-10; #BBDBB8; @neutral-90);
|
||||
border-radius: 4px;
|
||||
border: 1px solid #bbdbb8;
|
||||
box-shadow: none !important;
|
||||
|
||||
.notification-body {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.btn.btn-default-outline {
|
||||
border: 1px solid @neutral-60;
|
||||
background: white;
|
||||
font-size: 16px;
|
||||
color: @neutral-90;
|
||||
}
|
||||
|
||||
.btn.btn-default-outline:active {
|
||||
background: @neutral-40;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Plans Test
|
||||
*/
|
||||
|
|
|
@ -138,6 +138,12 @@
|
|||
"back_to_account_settings": "Back to account settings",
|
||||
"back_to_editor": "Back to the editor",
|
||||
"back_to_log_in": "Back to log in",
|
||||
"back_to_school_banner_bargain_with_x_percent_off_in_school_or_not": "Grab a back-to-school bargain with __x__% off all individual Overleaf subscriptions (whether you’re in school or not!)",
|
||||
"back_to_school_banner_hurry_offer_ends_sep_30": "Hurry! Offer ends September 30.",
|
||||
"back_to_school_homepage_bargain_with_x_percent_off": "Grab a back-to-school bargain with 15% off all individual Overleaf subscriptions",
|
||||
"back_to_school_modal_bargain_with_x_percent_off": "Everyone can grab a back-to-school bargain this fall with __x__% off an Overleaf subscription",
|
||||
"back_to_school_modal_offer_ends_sep_30": "Offer ends September 30.",
|
||||
"back_to_school_modal_offers_from_writefull_and_papers": "And with offers from Writefull and Papers too, you’ll have the tools you need to write smarter this year.",
|
||||
"back_to_subscription": "Back to Subscription",
|
||||
"back_to_your_projects": "Back to your projects",
|
||||
"become_an_advisor": "Become an __appName__ advisor",
|
||||
|
@ -229,6 +235,8 @@
|
|||
"choose_from_group_members": "Choose from Group Members",
|
||||
"choose_your_plan": "Choose your plan",
|
||||
"city": "City",
|
||||
"claim_discounts": "Claim discounts",
|
||||
"claim_my_discount": "Claim my discount",
|
||||
"clear_cached_files": "Clear cached files",
|
||||
"clear_search": "clear search",
|
||||
"clear_sessions": "Clear Sessions",
|
||||
|
|
BIN
services/web/public/img/subscriptions/back-to-school-modal.png
Normal file
BIN
services/web/public/img/subscriptions/back-to-school-modal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -78,6 +78,7 @@ describe('SubscriptionController', function () {
|
|||
buildPlansList: sinon.stub(),
|
||||
promises: {
|
||||
buildUsersSubscriptionViewModel: sinon.stub().resolves({}),
|
||||
getBestSubscription: sinon.stub().resolves({}),
|
||||
},
|
||||
buildPlansListForSubscriptionDash: sinon
|
||||
.stub()
|
||||
|
|
Loading…
Reference in a new issue