mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-29 20:11:32 +02:00
Merge pull request #22742 from overleaf/mf-remove-frontend-plans-page-dead-code
[web] Remove frontend plans page dead code GitOrigin-RevId: 6db07b909f99a7afd17880698787a2c3527e879f
This commit is contained in:
@@ -1,162 +0,0 @@
|
||||
import getMeta from '../../../utils/meta'
|
||||
import { swapModal } from '../../utils/swapModal'
|
||||
import * as eventTracking from '../../../infrastructure/event-tracking'
|
||||
import { createLocalizedGroupPlanPrice } from '../utils/group-plan-pricing'
|
||||
|
||||
export const GROUP_PLAN_MODAL_HASH = '#groups'
|
||||
|
||||
function getFormValues() {
|
||||
const modalEl = document.querySelector('[data-ol-group-plan-modal]')
|
||||
const planCode = modalEl.querySelector(
|
||||
'input[name="plan_code"]:checked'
|
||||
).value
|
||||
const size = modalEl.querySelector('#size').value
|
||||
const currency = modalEl.querySelector('#currency').value
|
||||
const usage = modalEl.querySelector('#usage').checked
|
||||
? 'educational'
|
||||
: 'enterprise'
|
||||
return { planCode, size, currency, usage }
|
||||
}
|
||||
|
||||
export function updateGroupModalPlanPricing() {
|
||||
const modalEl = document.querySelector('[data-ol-group-plan-modal]')
|
||||
const { planCode, size, currency, usage } = getFormValues()
|
||||
|
||||
const { localizedPrice, localizedPerUserPrice } =
|
||||
createLocalizedGroupPlanPrice({
|
||||
plan: planCode,
|
||||
licenseSize: size,
|
||||
currency,
|
||||
usage,
|
||||
})
|
||||
|
||||
modalEl.querySelectorAll('[data-ol-group-plan-plan-code]').forEach(el => {
|
||||
el.hidden = el.getAttribute('data-ol-group-plan-plan-code') !== planCode
|
||||
})
|
||||
modalEl.querySelectorAll('[data-ol-group-plan-usage]').forEach(el => {
|
||||
el.hidden = el.getAttribute('data-ol-group-plan-usage') !== usage
|
||||
})
|
||||
modalEl.querySelector('[data-ol-group-plan-display-price]').innerText =
|
||||
localizedPrice
|
||||
modalEl
|
||||
.querySelectorAll('[data-ol-group-plan-price-per-user]')
|
||||
.forEach(el => {
|
||||
el.innerText = `${localizedPerUserPrice} ${el.getAttribute(
|
||||
'data-ol-group-plan-price-per-user'
|
||||
)}`
|
||||
})
|
||||
|
||||
modalEl.querySelector('[data-ol-group-plan-educational-discount]').hidden =
|
||||
usage !== 'educational'
|
||||
|
||||
modalEl.querySelector(
|
||||
'[data-ol-group-plan-educational-discount-applied]'
|
||||
).hidden = size < 10
|
||||
|
||||
modalEl.querySelector(
|
||||
'[data-ol-group-plan-educational-discount-ineligible]'
|
||||
).hidden = size >= 10
|
||||
}
|
||||
|
||||
const modalEl = $('[data-ol-group-plan-modal]')
|
||||
modalEl
|
||||
.on('shown.bs.modal', function () {
|
||||
const path = `${window.location.pathname}${window.location.search}`
|
||||
history.replaceState(null, document.title, path + GROUP_PLAN_MODAL_HASH)
|
||||
eventTracking.sendMB('form-submitted-groups-modal-open')
|
||||
})
|
||||
.on('hidden.bs.modal', function () {
|
||||
const path = `${window.location.pathname}${window.location.search}${window.location.hash}`
|
||||
history.replaceState(null, document.title, path)
|
||||
})
|
||||
|
||||
function showGroupPlanModal() {
|
||||
modalEl.modal()
|
||||
eventTracking.send(
|
||||
'subscription-funnel',
|
||||
'plans-page',
|
||||
'group-inquiry-potential'
|
||||
) // deprecated by plans-page-click
|
||||
}
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-ol-group-plan-form] select')
|
||||
.forEach(el => el.addEventListener('change', updateGroupModalPlanPricing))
|
||||
document
|
||||
.querySelectorAll('[data-ol-group-plan-form] input')
|
||||
.forEach(el => el.addEventListener('change', updateGroupModalPlanPricing))
|
||||
document.querySelectorAll('[data-ol-purchase-group-plan]').forEach(el =>
|
||||
el.addEventListener('click', e => {
|
||||
e.preventDefault()
|
||||
|
||||
const { planCode, size, currency, usage } = getFormValues()
|
||||
const queryParams = new URLSearchParams(
|
||||
Object.entries({
|
||||
planCode: `group_${planCode}_${size}_${usage}`,
|
||||
currency,
|
||||
itm_campaign: 'groups',
|
||||
})
|
||||
)
|
||||
const itmContent = getMeta('ol-itm_content')
|
||||
if (itmContent) {
|
||||
queryParams.set('itm_content', itmContent)
|
||||
}
|
||||
eventTracking.sendMB('groups-modal-click', {
|
||||
plan: planCode,
|
||||
users: size,
|
||||
currency,
|
||||
type: usage,
|
||||
})
|
||||
const url = new URL('/user/subscription/new', window.origin)
|
||||
url.search = queryParams.toString()
|
||||
window.location = url.toString()
|
||||
})
|
||||
)
|
||||
|
||||
document.querySelectorAll('[data-ol-open-group-plan-modal]').forEach(el => {
|
||||
const location = el.getAttribute('data-ol-location')
|
||||
el.addEventListener('click', function (e) {
|
||||
e.preventDefault()
|
||||
eventTracking.sendMB('plans-page-click', {
|
||||
button: 'group',
|
||||
location,
|
||||
'billing-period': 'annual',
|
||||
})
|
||||
showGroupPlanModal()
|
||||
})
|
||||
})
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-ol-open-contact-form-for-more-than-50-licenses]')
|
||||
.forEach(el => {
|
||||
el.addEventListener('click', function (e) {
|
||||
e.preventDefault()
|
||||
swapModal(
|
||||
'[data-ol-group-plan-modal]',
|
||||
'[data-ol-contact-form-modal="general"]'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
function updateGroupModalPlanPricingIfAvailable() {
|
||||
const isGroupPlanModalAvailable = document.querySelector(
|
||||
'[data-ol-group-plan-modal]'
|
||||
)
|
||||
|
||||
if (isGroupPlanModalAvailable) {
|
||||
updateGroupModalPlanPricing()
|
||||
}
|
||||
}
|
||||
|
||||
updateGroupModalPlanPricingIfAvailable()
|
||||
|
||||
// When using browser back buttons, we need to update the pricing plan
|
||||
// after the page has fully loaded as we need to wait for the previously
|
||||
// selected values to load for e.g. size.
|
||||
window.addEventListener('load', () => {
|
||||
updateGroupModalPlanPricingIfAvailable()
|
||||
})
|
||||
|
||||
if (window.location.hash === GROUP_PLAN_MODAL_HASH) {
|
||||
showGroupPlanModal()
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
import './group-plan-modal'
|
||||
import { updateMainGroupPlanPricing } from '../../pages/user/subscription/plans-v2/plans-v2-group-plan'
|
||||
|
||||
export function changePlansV2MainPageGroupData() {
|
||||
const mainPlansPageFormEl = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-form]'
|
||||
)
|
||||
const mainPlansPageLicensePickerEl = mainPlansPageFormEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-select]'
|
||||
)
|
||||
|
||||
const mainPlansPageEducationalDiscountEl = mainPlansPageFormEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
)
|
||||
|
||||
const groupPlanModalNumberOfLicenses = document.querySelector(
|
||||
'[data-ol-group-plan-modal] #size'
|
||||
).value
|
||||
|
||||
const educationalDiscountChecked = document.querySelector(
|
||||
'[data-ol-group-plan-modal] #usage'
|
||||
).checked
|
||||
|
||||
const educationalDiscountEnabled =
|
||||
educationalDiscountChecked && groupPlanModalNumberOfLicenses >= 10
|
||||
|
||||
// update license picker on the main plans page
|
||||
mainPlansPageLicensePickerEl.value = groupPlanModalNumberOfLicenses
|
||||
|
||||
// update educational discount checkbox on the main plans page
|
||||
//
|
||||
// extra note
|
||||
// for number of users < 10, there is a difference on the checkbox behaviour
|
||||
// between the group plan modal and the main plan page
|
||||
//
|
||||
// On the group plan modal, the checkbox button is not visually disabled for number of users < 10 (checkbox can still be clicked)
|
||||
// but the logic is disabled and there will be an extra text whether or not the discount is applied
|
||||
//
|
||||
// However, on the main group plan page, the checkbox button is visually disabled for number of users < 10 (checkbox can not be clicked)
|
||||
// Hence, there's a possibility that the checkbox on the group plan modal is checked, but the discount is not applied.
|
||||
// i.e user can still click the checkbox with number of users < 10. The price won't be discounted, but the checkbox is checked.
|
||||
if (groupPlanModalNumberOfLicenses >= 10) {
|
||||
mainPlansPageEducationalDiscountEl.checked = educationalDiscountEnabled
|
||||
} else {
|
||||
// The code below is for disabling the checkbox button on the main plan page for number of users <10
|
||||
// while still checking the educational discount
|
||||
if (educationalDiscountChecked) {
|
||||
mainPlansPageEducationalDiscountEl.checked = false
|
||||
}
|
||||
}
|
||||
|
||||
updateMainGroupPlanPricing()
|
||||
}
|
||||
|
||||
function hideCurrencyPicker() {
|
||||
document.querySelector('[data-ol-group-plan-form-currency]').hidden = true
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-ol-group-plan-form] select').forEach(el =>
|
||||
el.addEventListener('change', () => {
|
||||
changePlansV2MainPageGroupData()
|
||||
})
|
||||
)
|
||||
document
|
||||
.querySelectorAll('[data-ol-group-plan-form] input')
|
||||
.forEach(el => el.addEventListener('change', changePlansV2MainPageGroupData))
|
||||
|
||||
const isGroupPlanModalAvailable = document.querySelector(
|
||||
'[data-ol-group-plan-modal]'
|
||||
)
|
||||
|
||||
if (isGroupPlanModalAvailable) {
|
||||
hideCurrencyPicker()
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import { formatCurrency } from '@/shared/utils/currency'
|
||||
import getMeta from '../../../utils/meta'
|
||||
|
||||
/**
|
||||
* @import { CurrencyCode } from '../../../../../types/subscription/currency'
|
||||
*/
|
||||
|
||||
// plan: 'collaborator' or 'professional'
|
||||
// the rest of available arguments can be seen in the groupPlans value
|
||||
/**
|
||||
* @param {Object} opts
|
||||
* @param {'collaborator' | 'professional'} opts.plan
|
||||
* @param {string} opts.licenseSize
|
||||
* @param {CurrencyCode} opts.currency
|
||||
* @param {'enterprise' | 'educational'} opts.usage
|
||||
* @param {string} [opts.locale]
|
||||
* @returns {{localizedPrice: string, localizedPerUserPrice: string}}
|
||||
*/
|
||||
export function createLocalizedGroupPlanPrice({
|
||||
plan,
|
||||
licenseSize,
|
||||
currency,
|
||||
usage,
|
||||
locale = getMeta('ol-i18n').currentLangCode || 'en',
|
||||
}) {
|
||||
const groupPlans = getMeta('ol-groupPlans')
|
||||
const priceInCents =
|
||||
groupPlans[usage][plan][currency][licenseSize].price_in_cents
|
||||
|
||||
const price = priceInCents / 100
|
||||
const perUserPrice = price / parseInt(licenseSize)
|
||||
|
||||
/**
|
||||
* @param {number} price
|
||||
* @returns {string}
|
||||
*/
|
||||
const formatPrice = price => formatCurrency(price, currency, locale, true)
|
||||
|
||||
return {
|
||||
localizedPrice: formatPrice(price),
|
||||
localizedPerUserPrice: formatPrice(perUserPrice),
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
import '../../../../features/plans/plans-v2-group-plan-modal'
|
||||
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import { updateGroupModalPlanPricing } from '../../../../features/plans/group-plan-modal'
|
||||
import { createLocalizedGroupPlanPrice } from '../../../../features/plans/utils/group-plan-pricing'
|
||||
|
||||
const MINIMUM_LICENSE_SIZE_EDUCATIONAL_DISCOUNT = 10
|
||||
|
||||
export function updateMainGroupPlanPricing() {
|
||||
const currency = getMeta('ol-recommendedCurrency')
|
||||
|
||||
const formEl = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-form]'
|
||||
)
|
||||
const licenseSize = formEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-select]'
|
||||
).value
|
||||
|
||||
const usage = formEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
).checked
|
||||
? 'educational'
|
||||
: 'enterprise'
|
||||
|
||||
const {
|
||||
localizedPrice: localizedPriceProfessional,
|
||||
localizedPerUserPrice: localizedPerUserPriceProfessional,
|
||||
} = createLocalizedGroupPlanPrice({
|
||||
plan: 'professional',
|
||||
licenseSize,
|
||||
currency,
|
||||
usage,
|
||||
})
|
||||
|
||||
const {
|
||||
localizedPrice: localizedPriceCollaborator,
|
||||
localizedPerUserPrice: localizedPerUserPriceCollaborator,
|
||||
} = createLocalizedGroupPlanPrice({
|
||||
plan: 'collaborator',
|
||||
licenseSize,
|
||||
currency,
|
||||
usage,
|
||||
})
|
||||
|
||||
document.querySelector(
|
||||
'[data-ol-plans-v2-group-total-price="professional"]'
|
||||
).innerText = localizedPriceProfessional
|
||||
|
||||
document.querySelector(
|
||||
'[data-ol-plans-v2-group-price-per-user="professional"]'
|
||||
).innerText = localizedPerUserPriceProfessional
|
||||
|
||||
document.querySelector(
|
||||
'[data-ol-plans-v2-group-total-price="collaborator"]'
|
||||
).innerText = localizedPriceCollaborator
|
||||
|
||||
document.querySelector(
|
||||
'[data-ol-plans-v2-group-price-per-user="collaborator"]'
|
||||
).innerText = localizedPerUserPriceCollaborator
|
||||
|
||||
const notEligibleForEducationalDiscount =
|
||||
licenseSize < MINIMUM_LICENSE_SIZE_EDUCATIONAL_DISCOUNT
|
||||
|
||||
formEl
|
||||
.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-label]'
|
||||
)
|
||||
.classList.toggle('disabled', notEligibleForEducationalDiscount)
|
||||
|
||||
formEl
|
||||
.querySelector('.plans-v2-license-picker-educational-discount')
|
||||
.classList.toggle(
|
||||
'total-licenses-not-eligible-for-discount',
|
||||
notEligibleForEducationalDiscount
|
||||
)
|
||||
|
||||
formEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
).disabled = notEligibleForEducationalDiscount
|
||||
|
||||
if (notEligibleForEducationalDiscount) {
|
||||
// force disable educational discount checkbox
|
||||
formEl.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
).checked = false
|
||||
}
|
||||
|
||||
changeNumberOfUsersInTableHead()
|
||||
changeNumberOfUsersInFeatureTable()
|
||||
}
|
||||
|
||||
export function changeGroupPlanModalNumberOfLicenses() {
|
||||
const modalEl = document.querySelector('[data-ol-group-plan-modal]')
|
||||
const numberOfLicenses = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-select]'
|
||||
).value
|
||||
|
||||
const groupPlanModalLicensePickerEl = modalEl.querySelector('#size')
|
||||
|
||||
groupPlanModalLicensePickerEl.value = numberOfLicenses
|
||||
updateGroupModalPlanPricing()
|
||||
}
|
||||
|
||||
export function changeGroupPlanModalEducationalDiscount() {
|
||||
const modalEl = document.querySelector('[data-ol-group-plan-modal]')
|
||||
const groupPlanModalEducationalDiscountEl = modalEl.querySelector('#usage')
|
||||
const educationalDiscountChecked = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
).checked
|
||||
|
||||
groupPlanModalEducationalDiscountEl.checked = educationalDiscountChecked
|
||||
updateGroupModalPlanPricing()
|
||||
}
|
||||
|
||||
export function changeNumberOfUsersInFeatureTable() {
|
||||
document
|
||||
.querySelectorAll(
|
||||
'[data-ol-plans-v2-table-cell-plan^="group"][data-ol-plans-v2-table-cell-feature="number_of_users"]'
|
||||
)
|
||||
.forEach(el => {
|
||||
const licenseSize = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-select]'
|
||||
).value
|
||||
|
||||
el.textContent = el.textContent.replace(/\d+/, licenseSize)
|
||||
})
|
||||
}
|
||||
|
||||
export function changeNumberOfUsersInTableHead() {
|
||||
document
|
||||
.querySelectorAll('[data-ol-plans-v2-table-th-group-license-size]')
|
||||
.forEach(el => {
|
||||
const licenseSize = el.getAttribute(
|
||||
'data-ol-plans-v2-table-th-group-license-size'
|
||||
)
|
||||
const currentLicenseSize = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-select]'
|
||||
).value
|
||||
|
||||
el.hidden = licenseSize !== currentLicenseSize
|
||||
})
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { GROUP_PLAN_MODAL_HASH } from '@/features/plans/group-plan-modal'
|
||||
|
||||
export function getViewInfoFromHash() {
|
||||
const hashValue = window.location.hash.replace('#', '')
|
||||
|
||||
const groupPlanModalHashValue = GROUP_PLAN_MODAL_HASH.replace('#', '')
|
||||
|
||||
switch (hashValue) {
|
||||
case 'individual-monthly':
|
||||
return ['individual', 'monthly']
|
||||
case 'individual-annual':
|
||||
return ['individual', 'annual']
|
||||
case groupPlanModalHashValue:
|
||||
case 'group':
|
||||
return ['group', 'annual']
|
||||
case 'student-monthly':
|
||||
return ['student', 'monthly']
|
||||
case 'student-annual':
|
||||
return ['student', 'annual']
|
||||
default:
|
||||
return ['individual', 'monthly']
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {individual | group | student} viewTab
|
||||
* @param {monthly | annual} period
|
||||
*/
|
||||
export function setHashFromViewTab(viewTab, period) {
|
||||
const newHash = viewTab === 'group' ? 'group' : `${viewTab}-${period}`
|
||||
if (window.location.hash.replace('#', '') !== newHash) {
|
||||
window.location.hash = newHash
|
||||
}
|
||||
}
|
||||
|
||||
// this is only for the students link in footer
|
||||
export function handleForStudentsLinkInFooter() {
|
||||
const links = document.querySelectorAll('[data-ol-for-students-link]')
|
||||
|
||||
links.forEach(function (link) {
|
||||
link.addEventListener('click', function () {
|
||||
if (window.location.pathname === '/user/subscription/plans') {
|
||||
// reload location with the correct hash
|
||||
const newURL =
|
||||
'/user/subscription/plans?itm_referrer=footer-for-students#student-annual'
|
||||
history.replaceState(null, '', newURL)
|
||||
location.reload()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// m-a stands for monthly-annual
|
||||
|
||||
export function toggleMonthlyAnnualSwitching(
|
||||
view,
|
||||
currentMonthlyAnnualSwitchValue
|
||||
) {
|
||||
const containerEl = document.querySelector(
|
||||
'[data-ol-plans-v2-m-a-switch-container]'
|
||||
)
|
||||
if (containerEl) {
|
||||
const checkbox = containerEl.querySelector('input[type="checkbox"]')
|
||||
|
||||
containerEl.classList.toggle('disabled', view === 'group')
|
||||
|
||||
checkbox.disabled = view === 'group'
|
||||
checkbox.checked = currentMonthlyAnnualSwitchValue === 'monthly'
|
||||
|
||||
switchMonthlyAnnual(currentMonthlyAnnualSwitchValue)
|
||||
}
|
||||
}
|
||||
|
||||
export function switchMonthlyAnnual(currentMonthlyAnnualSwitchValue) {
|
||||
const el = document.querySelector('[data-ol-plans-v2-m-a-tooltip]')
|
||||
el.classList.toggle(
|
||||
'plans-v2-m-a-tooltip-monthly-selected',
|
||||
currentMonthlyAnnualSwitchValue === 'monthly'
|
||||
)
|
||||
|
||||
document.querySelectorAll('[data-ol-tooltip-period]').forEach(el => {
|
||||
const period = el.getAttribute('data-ol-tooltip-period')
|
||||
el.hidden = period !== currentMonthlyAnnualSwitchValue
|
||||
})
|
||||
|
||||
document.querySelectorAll('[data-ol-plans-v2-period').forEach(el => {
|
||||
const period = el.getAttribute('data-ol-plans-v2-period')
|
||||
|
||||
el.hidden = currentMonthlyAnnualSwitchValue !== period
|
||||
})
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-ol-plans-v2-m-a-switch-text]')
|
||||
.forEach(el => {
|
||||
el.classList.toggle(
|
||||
'underline',
|
||||
el.getAttribute('data-ol-plans-v2-m-a-switch-text') ===
|
||||
currentMonthlyAnnualSwitchValue
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function changeMonthlyAnnualTooltipPosition() {
|
||||
const smallScreen = window.matchMedia('(max-width: 767px)').matches
|
||||
const el = document.querySelector('[data-ol-plans-v2-m-a-tooltip]')
|
||||
|
||||
el.classList.toggle('bottom', smallScreen)
|
||||
el.classList.toggle('left', !smallScreen)
|
||||
}
|
||||
|
||||
// click event listener for monthly-annual switch
|
||||
export function setUpMonthlyAnnualSwitching() {
|
||||
changeMonthlyAnnualTooltipPosition()
|
||||
window.addEventListener('resize', changeMonthlyAnnualTooltipPosition)
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
import '../../../../marketing'
|
||||
import '../../../../infrastructure/hotjar'
|
||||
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import { setUpStickyHeaderObserver } from './plans-v2-sticky-header'
|
||||
import {
|
||||
setUpMonthlyAnnualSwitching,
|
||||
switchMonthlyAnnual,
|
||||
toggleMonthlyAnnualSwitching,
|
||||
} from './plans-v2-m-a-switch'
|
||||
import {
|
||||
changeGroupPlanModalEducationalDiscount,
|
||||
changeGroupPlanModalNumberOfLicenses,
|
||||
updateMainGroupPlanPricing,
|
||||
} from './plans-v2-group-plan'
|
||||
import { setUpGroupSubscriptionButtonAction } from './plans-v2-subscription-button'
|
||||
import {
|
||||
getViewInfoFromHash,
|
||||
handleForStudentsLinkInFooter,
|
||||
setHashFromViewTab,
|
||||
} from './plans-v2-hash'
|
||||
import { sendPlansViewEvent } from './plans-v2-tracking'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
|
||||
const currentCurrencyCode = getMeta('ol-recommendedCurrency')
|
||||
|
||||
function showQuoteForTab(viewTab) {
|
||||
// hide/display quote rows
|
||||
document.querySelectorAll('.plans-page-quote-row').forEach(quoteRow => {
|
||||
const showForPlanTypes = quoteRow.getAttribute('data-ol-show-for-plan-type')
|
||||
if (showForPlanTypes?.includes(viewTab)) {
|
||||
quoteRow.classList.remove('plans-page-quote-row-hidden')
|
||||
} else {
|
||||
quoteRow.classList.add('plans-page-quote-row-hidden')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setUpSubscriptionTracking(linkEl) {
|
||||
linkEl.addEventListener('click', function () {
|
||||
const plan =
|
||||
linkEl.getAttribute('data-ol-tracking-plan') ||
|
||||
linkEl.getAttribute('data-ol-start-new-subscription')
|
||||
|
||||
const location = linkEl.getAttribute('data-ol-location')
|
||||
const period = linkEl.getAttribute('data-ol-item-view')
|
||||
|
||||
const DEFAULT_EVENT_TRACKING_KEY = 'plans-page-click'
|
||||
|
||||
const eventTrackingKey =
|
||||
linkEl.getAttribute('data-ol-event-tracking-key') ||
|
||||
DEFAULT_EVENT_TRACKING_KEY
|
||||
|
||||
const eventTrackingSegmentation = {
|
||||
button: plan,
|
||||
location,
|
||||
'billing-period': period,
|
||||
currency: currentCurrencyCode,
|
||||
}
|
||||
|
||||
eventTracking.sendMB(eventTrackingKey, eventTrackingSegmentation)
|
||||
})
|
||||
}
|
||||
|
||||
const searchParams = new URLSearchParams(window.location.search)
|
||||
|
||||
export function updateLinkTargets() {
|
||||
document.querySelectorAll('[data-ol-start-new-subscription]').forEach(el => {
|
||||
if (el.hasAttribute('data-ol-has-custom-href')) return
|
||||
|
||||
const plan = el.getAttribute('data-ol-start-new-subscription')
|
||||
const view = el.getAttribute('data-ol-item-view')
|
||||
|
||||
const suffix = view === 'annual' ? '-annual' : '_free_trial_7_days'
|
||||
|
||||
const planCode = `${plan}${suffix}`
|
||||
|
||||
const location = el.getAttribute('data-ol-location')
|
||||
const itmCampaign = searchParams.get('itm_campaign') || 'plans'
|
||||
const itmContent =
|
||||
itmCampaign === 'plans' ? location : searchParams.get('itm_content')
|
||||
|
||||
const queryString = new URLSearchParams({
|
||||
planCode,
|
||||
currency: currentCurrencyCode,
|
||||
itm_campaign: itmCampaign,
|
||||
})
|
||||
|
||||
if (itmContent) {
|
||||
queryString.set('itm_content', itmContent)
|
||||
}
|
||||
|
||||
if (searchParams.get('itm_referrer')) {
|
||||
queryString.set('itm_referrer', searchParams.get('itm_referrer'))
|
||||
}
|
||||
|
||||
el.href = `/user/subscription/new?${queryString.toString()}`
|
||||
})
|
||||
}
|
||||
|
||||
// We need this mutable variable because the group tab only have annual.
|
||||
// There's some difference between the monthly and annual UI
|
||||
// and since monthly-annual switch is disabled for the group tab,
|
||||
// we need to introduce a new variable to store the information
|
||||
let currentMonthlyAnnualSwitchValue = 'annual'
|
||||
|
||||
function selectTab(viewTab) {
|
||||
document.querySelectorAll('[data-ol-plans-v2-view-tab]').forEach(el => {
|
||||
const tab = el.querySelector('[data-ol-plans-v2-view-tab] button')
|
||||
if (tab) {
|
||||
const isActive =
|
||||
tab.parentElement.getAttribute('data-ol-plans-v2-view-tab') === viewTab
|
||||
tab.parentElement.classList.toggle('active', isActive)
|
||||
tab.setAttribute('aria-selected', isActive)
|
||||
}
|
||||
})
|
||||
|
||||
document.querySelectorAll('[data-ol-plans-v2-view]').forEach(el => {
|
||||
el.hidden = el.getAttribute('data-ol-plans-v2-view') !== viewTab
|
||||
})
|
||||
|
||||
const tooltipEl = document.querySelector('[data-ol-plans-v2-m-a-tooltip]')
|
||||
if (tooltipEl) {
|
||||
tooltipEl.hidden = viewTab === 'group'
|
||||
}
|
||||
|
||||
const licensePickerEl = document.querySelector(
|
||||
'[data-ol-plans-v2-license-picker-container]'
|
||||
)
|
||||
if (licensePickerEl) {
|
||||
licensePickerEl.hidden = viewTab !== 'group'
|
||||
}
|
||||
|
||||
const monthlyAnnualSwitch = document.querySelector(
|
||||
'[data-ol-plans-v2-m-a-switch-container]'
|
||||
)
|
||||
if (monthlyAnnualSwitch) {
|
||||
monthlyAnnualSwitch.setAttribute('data-ol-current-view', viewTab)
|
||||
}
|
||||
|
||||
if (viewTab === 'group') {
|
||||
updateMainGroupPlanPricing()
|
||||
}
|
||||
|
||||
updateMonthlyAnnualSwitchValue(viewTab)
|
||||
|
||||
toggleUniversityInfo(viewTab)
|
||||
|
||||
// update the hash to reflect the current view when switching individual, group, or student tabs
|
||||
setHashFromViewTab(viewTab, currentMonthlyAnnualSwitchValue)
|
||||
|
||||
showQuoteForTab(viewTab)
|
||||
}
|
||||
|
||||
function updateMonthlyAnnualSwitchValue(viewTab) {
|
||||
// group tab is special because group plan only has annual value
|
||||
// so we need to perform some UI changes whenever user click the group tab
|
||||
if (viewTab === 'group') {
|
||||
toggleMonthlyAnnualSwitching(viewTab, 'annual')
|
||||
} else {
|
||||
toggleMonthlyAnnualSwitching(viewTab, currentMonthlyAnnualSwitchValue)
|
||||
}
|
||||
}
|
||||
|
||||
function setUpTabSwitching() {
|
||||
document.querySelectorAll('[data-ol-plans-v2-view-tab]').forEach(el => {
|
||||
const viewTab = el.getAttribute('data-ol-plans-v2-view-tab')
|
||||
|
||||
el.querySelector('button').addEventListener('click', function (e) {
|
||||
e.preventDefault()
|
||||
eventTracking.send(
|
||||
'subscription-funnel',
|
||||
'plans-page',
|
||||
`${viewTab}-prices`
|
||||
)
|
||||
selectTab(viewTab)
|
||||
})
|
||||
})
|
||||
|
||||
const tabs = document.querySelectorAll(
|
||||
'[data-ol-plans-v2-view-tab] [role="tab"]'
|
||||
)
|
||||
|
||||
if (tabs) {
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('keydown', event => {
|
||||
if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {
|
||||
const currentIndex = Array.from(tabs).indexOf(tab)
|
||||
const nextIndex =
|
||||
event.key === 'ArrowLeft' ? currentIndex - 1 : currentIndex + 1
|
||||
const newIndex = (nextIndex + tabs.length) % tabs.length
|
||||
tabs[newIndex].focus()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function setUpGroupPlanPricingChange() {
|
||||
document
|
||||
.querySelectorAll('[data-ol-plans-v2-license-picker-select]')
|
||||
.forEach(el => {
|
||||
el.addEventListener('change', () => {
|
||||
updateMainGroupPlanPricing()
|
||||
changeGroupPlanModalNumberOfLicenses()
|
||||
})
|
||||
})
|
||||
|
||||
document
|
||||
.querySelectorAll(
|
||||
'[data-ol-plans-v2-license-picker-educational-discount-input]'
|
||||
)
|
||||
.forEach(el =>
|
||||
el.addEventListener('change', () => {
|
||||
updateMainGroupPlanPricing()
|
||||
changeGroupPlanModalEducationalDiscount()
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
function toggleUniversityInfo(viewTab) {
|
||||
const el = document.querySelector('[data-ol-plans-university-info-container]')
|
||||
if (el) {
|
||||
el.hidden = viewTab !== 'student'
|
||||
}
|
||||
}
|
||||
|
||||
// This is the old scheme for hashing redirection
|
||||
// This is deprecated and should be removed in the future
|
||||
// This is only used for backward compatibility
|
||||
function selectViewFromHashDeprecated() {
|
||||
try {
|
||||
const params = new URLSearchParams(window.location.hash.substring(1))
|
||||
const view = params.get('view')
|
||||
if (view) {
|
||||
// View params are expected to be of the format e.g. individual or individual-monthly
|
||||
const [tab, period] = view.split('-')
|
||||
// make sure the selected view is valid
|
||||
if (document.querySelector(`[data-ol-plans-v2-view-tab="${tab}"]`)) {
|
||||
selectTab(tab)
|
||||
|
||||
if (['monthly', 'annual'].includes(period)) {
|
||||
currentMonthlyAnnualSwitchValue = period
|
||||
} else {
|
||||
// set annual as the default
|
||||
currentMonthlyAnnualSwitchValue = 'annual'
|
||||
}
|
||||
|
||||
updateMonthlyAnnualSwitchValue(tab)
|
||||
|
||||
// change the hash with the new scheme
|
||||
setHashFromViewTab(tab, currentMonthlyAnnualSwitchValue)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
function selectViewAndPeriodFromHash() {
|
||||
const [viewTab, period] = getViewInfoFromHash()
|
||||
|
||||
// the sequence of these three lines is important
|
||||
// because `currentMonthlyAnnualSwitchValue` is mutable.
|
||||
// `selectTab` and `updateMonthlyAnnualSwitchValue` depend on the value of `currentMonthlyAnnualSwitchValue`
|
||||
// to determine the UI state
|
||||
currentMonthlyAnnualSwitchValue = period
|
||||
selectTab(viewTab)
|
||||
updateMonthlyAnnualSwitchValue(viewTab)
|
||||
|
||||
// handle the case where user access plans page while still on the plans page
|
||||
// current example would the the "For students" link on the footer
|
||||
const SCROLL_TO_TOP_DELAY = 50
|
||||
window.setTimeout(() => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}, SCROLL_TO_TOP_DELAY)
|
||||
}
|
||||
|
||||
// call the function to select the view and period from the hash value
|
||||
// this is called once when the page is loaded
|
||||
if (window.location.hash) {
|
||||
if (window.location.hash.includes('view')) {
|
||||
selectViewFromHashDeprecated()
|
||||
} else {
|
||||
selectViewAndPeriodFromHash()
|
||||
}
|
||||
}
|
||||
|
||||
document
|
||||
.querySelector('[data-ol-plans-v2-m-a-switch]')
|
||||
.addEventListener('click', () => {
|
||||
const isMonthlyPricing = document.querySelector(
|
||||
'[data-ol-plans-v2-m-a-switch] input[type="checkbox"]'
|
||||
).checked
|
||||
|
||||
if (isMonthlyPricing) {
|
||||
currentMonthlyAnnualSwitchValue = 'monthly'
|
||||
} else {
|
||||
currentMonthlyAnnualSwitchValue = 'annual'
|
||||
}
|
||||
|
||||
switchMonthlyAnnual(currentMonthlyAnnualSwitchValue)
|
||||
|
||||
// update the hash to reflect the current view when pressing the monthly-annual switch
|
||||
const DEFAULT_VIEW_TAB = 'individual'
|
||||
const viewTab =
|
||||
document
|
||||
.querySelector('[data-ol-plans-v2-m-a-switch-container]')
|
||||
.getAttribute('data-ol-current-view') ?? DEFAULT_VIEW_TAB
|
||||
|
||||
setHashFromViewTab(viewTab, currentMonthlyAnnualSwitchValue)
|
||||
})
|
||||
|
||||
document
|
||||
.querySelectorAll('[data-ol-start-new-subscription]')
|
||||
.forEach(setUpSubscriptionTracking)
|
||||
|
||||
setUpTabSwitching()
|
||||
setUpGroupPlanPricingChange()
|
||||
setUpMonthlyAnnualSwitching()
|
||||
setUpGroupSubscriptionButtonAction()
|
||||
setUpStickyHeaderObserver()
|
||||
updateLinkTargets()
|
||||
handleForStudentsLinkInFooter()
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
if (window.location.hash) {
|
||||
if (window.location.hash.includes('view')) {
|
||||
selectViewFromHashDeprecated()
|
||||
} else {
|
||||
selectViewAndPeriodFromHash()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
sendPlansViewEvent()
|
||||
@@ -1,17 +0,0 @@
|
||||
function stickyHeaderObserverCallback(entry) {
|
||||
document
|
||||
.querySelectorAll('[data-ol-plans-v2-table-sticky-header]')
|
||||
.forEach(el =>
|
||||
el.classList.toggle('sticky', entry[0].boundingClientRect.bottom > 0)
|
||||
)
|
||||
}
|
||||
|
||||
export function setUpStickyHeaderObserver() {
|
||||
const stickyHeaderStopEl = document.querySelector(
|
||||
'[data-ol-plans-v2-table-sticky-header-stop]'
|
||||
)
|
||||
|
||||
const observer = new IntersectionObserver(stickyHeaderObserverCallback)
|
||||
|
||||
observer.observe(stickyHeaderStopEl)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { updateGroupModalPlanPricing } from '../../../../features/plans/group-plan-modal'
|
||||
|
||||
function showGroupPlanModal(el) {
|
||||
const plan = el.getAttribute('data-ol-start-new-subscription')
|
||||
|
||||
// plan is either `group_collaborator` or `group_professional`
|
||||
// we want to get the suffix (collaborator or professional)
|
||||
const groupPlan = plan.split('_')[1]
|
||||
|
||||
const groupModalRadioInputEl = document.querySelector(
|
||||
`[data-ol-group-plan-code="${groupPlan}"]`
|
||||
)
|
||||
|
||||
groupModalRadioInputEl.checked = true
|
||||
updateGroupModalPlanPricing()
|
||||
|
||||
const modalEl = $('[data-ol-group-plan-modal]')
|
||||
modalEl.modal()
|
||||
}
|
||||
|
||||
export function setUpGroupSubscriptionButtonAction() {
|
||||
document.querySelectorAll('[data-ol-start-new-subscription]').forEach(el => {
|
||||
const plan = el.getAttribute('data-ol-start-new-subscription')
|
||||
|
||||
if (plan === 'group_collaborator' || plan === 'group_professional') {
|
||||
el.addEventListener('click', e => {
|
||||
e.preventDefault()
|
||||
showGroupPlanModal(el)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { sendMB } from '@/infrastructure/event-tracking'
|
||||
import { getSplitTestVariant } from '@/utils/splitTestUtils'
|
||||
import getMeta from '@/utils/meta'
|
||||
|
||||
export function sendPlansViewEvent() {
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
function () {
|
||||
const currency = getMeta('ol-recommendedCurrency')
|
||||
const countryCode = getMeta('ol-countryCode')
|
||||
|
||||
const groupTabImprovementsVariant = getSplitTestVariant(
|
||||
'group-tab-improvements'
|
||||
)
|
||||
|
||||
const periodToggleTestVariant = getSplitTestVariant(
|
||||
'period-toggle-improvements'
|
||||
)
|
||||
|
||||
const device = window.matchMedia('(max-width: 767px)').matches
|
||||
? 'mobile'
|
||||
: 'desktop'
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search)
|
||||
const planTabParam = queryParams.get('plan')
|
||||
|
||||
const plansPageViewSegmentation = {
|
||||
currency,
|
||||
countryCode,
|
||||
device,
|
||||
'group-tab-improvements': groupTabImprovementsVariant,
|
||||
plan: planTabParam,
|
||||
'period-toggle-improvements': periodToggleTestVariant,
|
||||
}
|
||||
|
||||
const isPlansPage = window.location.href.includes(
|
||||
'user/subscription/plans'
|
||||
)
|
||||
const isInterstitialPaymentPage = window.location.href.includes(
|
||||
'user/subscription/choose-your-plan'
|
||||
)
|
||||
|
||||
if (isPlansPage) {
|
||||
sendMB('plans-page-view', plansPageViewSegmentation)
|
||||
} else if (isInterstitialPaymentPage) {
|
||||
sendMB('paywall-plans-page-view', plansPageViewSegmentation)
|
||||
}
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
import { expect } from 'chai'
|
||||
import { createLocalizedGroupPlanPrice } from '../../../../frontend/js/features/plans/utils/group-plan-pricing'
|
||||
|
||||
describe('group-plan-pricing', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-groupPlans', {
|
||||
enterprise: {
|
||||
professional: {
|
||||
CHF: {
|
||||
2: {
|
||||
price_in_cents: 10000,
|
||||
},
|
||||
},
|
||||
DKK: {
|
||||
2: {
|
||||
price_in_cents: 20000,
|
||||
},
|
||||
},
|
||||
USD: {
|
||||
2: {
|
||||
price_in_cents: 30000,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
window.metaAttributesCache.set('ol-i18n', { currentLangCode: 'en' })
|
||||
})
|
||||
|
||||
describe('createLocalizedGroupPlanPrice', function () {
|
||||
describe('CHF currency', function () {
|
||||
it('should return the correct localized price', function () {
|
||||
const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({
|
||||
plan: 'professional',
|
||||
currency: 'CHF',
|
||||
licenseSize: '2',
|
||||
usage: 'enterprise',
|
||||
})
|
||||
|
||||
expect(localizedGroupPlanPrice).to.deep.equal({
|
||||
localizedPrice: 'CHF 100',
|
||||
localizedPerUserPrice: 'CHF 50',
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('DKK currency', function () {
|
||||
it('should return the correct localized price', function () {
|
||||
const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({
|
||||
plan: 'professional',
|
||||
currency: 'DKK',
|
||||
licenseSize: '2',
|
||||
usage: 'enterprise',
|
||||
})
|
||||
|
||||
expect(localizedGroupPlanPrice).to.deep.equal({
|
||||
localizedPrice: 'kr 200',
|
||||
localizedPerUserPrice: 'kr 100',
|
||||
})
|
||||
})
|
||||
})
|
||||
describe('other supported currencies', function () {
|
||||
it('should return the correct localized price', function () {
|
||||
const localizedGroupPlanPrice = createLocalizedGroupPlanPrice({
|
||||
plan: 'professional',
|
||||
currency: 'USD',
|
||||
licenseSize: '2',
|
||||
usage: 'enterprise',
|
||||
})
|
||||
|
||||
expect(localizedGroupPlanPrice).to.deep.equal({
|
||||
localizedPrice: '$300',
|
||||
localizedPerUserPrice: '$150',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user