@@ -1,86 +0,0 @@
|
||||
import Backbone from 'backbone';
|
||||
import moment from 'moment';
|
||||
|
||||
import DateUtils from 'edx-ui-toolkit/js/utils/date-utils';
|
||||
import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
|
||||
|
||||
|
||||
/**
|
||||
* Model for Program Subscription Data.
|
||||
*/
|
||||
class ProgramSubscriptionModel extends Backbone.Model {
|
||||
constructor({ context }, ...args) {
|
||||
const {
|
||||
subscriptionData: [data = {}],
|
||||
programData: { subscription_prices },
|
||||
urls = {},
|
||||
userPreferences = {},
|
||||
subscriptionsTrialLength: trialLength = 7,
|
||||
} = context;
|
||||
|
||||
const priceInUSD = subscription_prices?.find(({ currency }) => currency === 'USD');
|
||||
|
||||
const subscriptionState = data.subscription_state?.toLowerCase() ?? '';
|
||||
const subscriptionPrice = StringUtils.interpolate(
|
||||
gettext('${price}/month {currency}'),
|
||||
{
|
||||
price: parseFloat(priceInUSD?.price),
|
||||
currency: priceInUSD?.currency,
|
||||
}
|
||||
);
|
||||
|
||||
const subscriptionUrl =
|
||||
subscriptionState === 'active'
|
||||
? urls.manage_subscription_url
|
||||
: urls.buy_subscription_url;
|
||||
|
||||
const hasActiveTrial = false;
|
||||
|
||||
const remainingDays = 0;
|
||||
|
||||
const [currentPeriodEnd] = ProgramSubscriptionModel.formatDate(
|
||||
data.current_period_end,
|
||||
userPreferences
|
||||
);
|
||||
const [trialEndDate, trialEndTime] = ['', ''];
|
||||
|
||||
super(
|
||||
{
|
||||
hasActiveTrial,
|
||||
currentPeriodEnd,
|
||||
remainingDays,
|
||||
subscriptionPrice,
|
||||
subscriptionState,
|
||||
subscriptionUrl,
|
||||
trialEndDate,
|
||||
trialEndTime,
|
||||
trialLength,
|
||||
},
|
||||
...args
|
||||
);
|
||||
}
|
||||
|
||||
static formatDate(date, userPreferences) {
|
||||
if (!date) {
|
||||
return ['', ''];
|
||||
}
|
||||
|
||||
const userTimezone = (
|
||||
userPreferences.time_zone || moment?.tz?.guess?.() || 'UTC'
|
||||
);
|
||||
const userLanguage = userPreferences['pref-lang'] || 'en';
|
||||
const context = {
|
||||
datetime: date,
|
||||
timezone: userTimezone,
|
||||
language: userLanguage,
|
||||
format: DateUtils.dateFormatEnum.shortDate,
|
||||
};
|
||||
|
||||
const localDate = DateUtils.localize(context);
|
||||
const localTime = '';
|
||||
|
||||
return [localDate, localTime];
|
||||
}
|
||||
}
|
||||
|
||||
export default ProgramSubscriptionModel;
|
||||
@@ -11,58 +11,18 @@ import HeaderView from './views/program_list_header_view';
|
||||
|
||||
function ProgramListFactory(options) {
|
||||
const progressCollection = new ProgressCollection();
|
||||
const subscriptionCollection = new Backbone.Collection();
|
||||
|
||||
if (options.userProgress) {
|
||||
progressCollection.set(options.userProgress);
|
||||
options.progressCollection = progressCollection; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
if (options.programsSubscriptionData.length) {
|
||||
subscriptionCollection.set(options.programsSubscriptionData);
|
||||
options.subscriptionCollection = subscriptionCollection; // eslint-disable-line no-param-reassign
|
||||
}
|
||||
|
||||
if (options.programsData.length) {
|
||||
if (!options.mobileOnly) {
|
||||
new HeaderView({
|
||||
context: options,
|
||||
}).render();
|
||||
}
|
||||
|
||||
const activeSubscriptions = options.programsSubscriptionData
|
||||
// eslint-disable-next-line camelcase
|
||||
.filter(({ subscription_state }) => subscription_state === 'active')
|
||||
.sort((a, b) => new Date(b.created) - new Date(a.created));
|
||||
|
||||
// Sort programs so programs with active subscriptions are at the top
|
||||
if (activeSubscriptions.length) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
options.programsData = options.programsData
|
||||
.map((programsData) => ({
|
||||
...programsData,
|
||||
subscriptionIndex: activeSubscriptions.findIndex(
|
||||
// eslint-disable-next-line camelcase
|
||||
({ resource_id }) => resource_id === programsData.uuid,
|
||||
),
|
||||
}))
|
||||
.sort(({ subscriptionIndex: indexA }, { subscriptionIndex: indexB }) => {
|
||||
switch (true) {
|
||||
case indexA === -1 && indexB === -1:
|
||||
// Maintain the original order for non-subscription programs
|
||||
return 0;
|
||||
case indexA === -1:
|
||||
// Move non-subscription program to the end
|
||||
return 1;
|
||||
case indexB === -1:
|
||||
// Keep non-subscription program to the end
|
||||
return -1;
|
||||
default:
|
||||
// Sort by subscriptionIndex in ascending order
|
||||
return indexA - indexB;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new CollectionListView({
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
/* globals setFixtures */
|
||||
|
||||
import Backbone from 'backbone';
|
||||
|
||||
import CollectionListView from '../views/collection_list_view';
|
||||
import ProgramCardView from '../views/program_card_view';
|
||||
import ProgramCollection from '../collections/program_collection';
|
||||
@@ -11,7 +9,6 @@ describe('Collection List View', () => {
|
||||
let view = null;
|
||||
let programCollection;
|
||||
let progressCollection;
|
||||
let subscriptionCollection;
|
||||
const context = {
|
||||
programsData: [
|
||||
{
|
||||
@@ -101,21 +98,14 @@ describe('Collection List View', () => {
|
||||
not_started: 3,
|
||||
},
|
||||
],
|
||||
programsSubscriptionData: [{
|
||||
resource_id: 'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8',
|
||||
subscription_state: 'active',
|
||||
}],
|
||||
isUserB2CSubscriptionsEnabled: false,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setFixtures('<div class="program-cards-container"></div>');
|
||||
programCollection = new ProgramCollection(context.programsData);
|
||||
progressCollection = new ProgressCollection();
|
||||
subscriptionCollection = new Backbone.Collection(context.programsSubscriptionData);
|
||||
progressCollection.set(context.userProgress);
|
||||
context.progressCollection = progressCollection;
|
||||
context.subscriptionCollection = subscriptionCollection;
|
||||
|
||||
view = new CollectionListView({
|
||||
el: '.program-cards-container',
|
||||
|
||||
@@ -17,10 +17,8 @@ describe('Course Card View', () => {
|
||||
programData,
|
||||
collectionCourseStatus,
|
||||
courseData: {},
|
||||
subscriptionData: [],
|
||||
urls: {},
|
||||
userPreferences: {},
|
||||
isSubscriptionEligible: false,
|
||||
};
|
||||
|
||||
if (typeof collectionCourseStatus === 'undefined') {
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/* globals setFixtures */
|
||||
|
||||
import ProgramAlertListView from '../views/program_alert_list_view';
|
||||
|
||||
describe('Program Alert List View', () => {
|
||||
let view = null;
|
||||
const context = {
|
||||
enrollmentAlerts: [{ title: 'Test Program' }],
|
||||
trialEndingAlerts: [{
|
||||
title: 'Test Program',
|
||||
hasActiveTrial: true,
|
||||
currentPeriodEnd: 'May 8, 2023',
|
||||
remainingDays: 2,
|
||||
subscriptionPrice: '$100/month USD',
|
||||
subscriptionState: 'active',
|
||||
subscriptionUrl: null,
|
||||
trialEndDate: 'Apr 20, 2023',
|
||||
trialEndTime: '5:59 am',
|
||||
trialLength: 7,
|
||||
}],
|
||||
pageType: 'programDetails',
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
setFixtures('<div class="js-program-details-alerts"></div>');
|
||||
view = new ProgramAlertListView({
|
||||
el: '.js-program-details-alerts',
|
||||
context,
|
||||
});
|
||||
view.render();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
view.remove();
|
||||
});
|
||||
|
||||
it('should exist', () => {
|
||||
expect(view).toBeDefined();
|
||||
});
|
||||
|
||||
it('should render no enrollement alert', () => {
|
||||
expect(view.$('.alert:first .alert-heading').text().trim()).toEqual(
|
||||
'Enroll in a Test Program\'s course',
|
||||
);
|
||||
expect(view.$('.alert:first .alert-message').text().trim()).toEqual(
|
||||
'You have an active subscription to the Test Program program but are not enrolled in any courses. Enroll in a remaining course and enjoy verified access.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render subscription trial is expiring alert', () => {
|
||||
expect(view.$('.alert:last .alert-heading').text().trim()).toEqual(
|
||||
'Subscription trial expires in 2 days',
|
||||
);
|
||||
expect(view.$('.alert:last .alert-message').text().trim()).toEqual(
|
||||
'Your Test Program trial will expire in 2 days at 5:59 am on Apr 20, 2023 and the card on file will be charged $100/month USD.',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -42,7 +42,6 @@ describe('Program card View', () => {
|
||||
name: 'Wageningen University & Research',
|
||||
},
|
||||
],
|
||||
subscriptionIndex: 1,
|
||||
};
|
||||
const userProgress = [
|
||||
{
|
||||
@@ -58,11 +57,6 @@ describe('Program card View', () => {
|
||||
not_started: 3,
|
||||
},
|
||||
];
|
||||
// eslint-disable-next-line no-undef
|
||||
const subscriptionCollection = new Backbone.Collection([{
|
||||
resource_id: 'a87e5eac-3c93-45a1-a8e1-4c79ca8401c8',
|
||||
subscription_state: 'active',
|
||||
}]);
|
||||
const progressCollection = new ProgressCollection();
|
||||
const cardRenders = ($card) => {
|
||||
expect($card).toBeDefined();
|
||||
@@ -80,8 +74,6 @@ describe('Program card View', () => {
|
||||
model: programModel,
|
||||
context: {
|
||||
progressCollection,
|
||||
subscriptionCollection,
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -133,10 +125,6 @@ describe('Program card View', () => {
|
||||
view.remove();
|
||||
view = new ProgramCardView({
|
||||
model: programModel,
|
||||
context: {
|
||||
subscriptionCollection,
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
},
|
||||
});
|
||||
cardRenders(view.$el);
|
||||
expect(view.$('.progress').length).toEqual(0);
|
||||
@@ -149,10 +137,6 @@ describe('Program card View', () => {
|
||||
programModel = new ProgramModel(programNoBanner);
|
||||
view = new ProgramCardView({
|
||||
model: programModel,
|
||||
context: {
|
||||
subscriptionCollection,
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
},
|
||||
});
|
||||
cardRenders(view.$el);
|
||||
expect(view.$el.find('.banner-image').attr('srcset')).toEqual('');
|
||||
@@ -167,16 +151,8 @@ describe('Program card View', () => {
|
||||
programModel = new ProgramModel(programNoBanner);
|
||||
view = new ProgramCardView({
|
||||
model: programModel,
|
||||
context: {
|
||||
subscriptionCollection,
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
},
|
||||
});
|
||||
cardRenders(view.$el);
|
||||
expect(view.$el.find('.banner-image').attr('srcset')).toEqual('');
|
||||
});
|
||||
|
||||
it('should render the subscription badge if subscription is active', () => {
|
||||
expect(view.$('.subscription-badge .badge').html()?.trim()).toEqual('Subscribed');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -45,16 +45,6 @@ describe('Program Details Header View', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
subscriptionData: [
|
||||
{
|
||||
trial_end: '1970-01-01T03:25:45Z',
|
||||
current_period_end: '1970-06-03T07:12:04Z',
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
subscription_state: 'active',
|
||||
},
|
||||
],
|
||||
isSubscriptionEligible: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -81,8 +71,4 @@ describe('Program Details Header View', () => {
|
||||
expect(view.$('.org-logo').attr('alt'))
|
||||
.toEqual(`${context.programData.authoring_organizations[0].name}'s logo`);
|
||||
});
|
||||
|
||||
it('should render the subscription badge if subscription is active', () => {
|
||||
expect(view.$('.meta-info .badge').html().trim()).toEqual('Subscribed');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/* globals setFixtures */
|
||||
|
||||
import Backbone from 'backbone';
|
||||
import moment from 'moment';
|
||||
|
||||
import SubscriptionModel from '../models/program_subscription_model';
|
||||
import ProgramSidebarView from '../views/program_details_sidebar_view';
|
||||
|
||||
describe('Program Progress View', () => {
|
||||
@@ -25,15 +23,13 @@ describe('Program Progress View', () => {
|
||||
"url": "/certificates/bed3980e67ca40f0b31e309d9dfe9e7e", "type": "course", "title": "Introduction to the Treatment of Urban Sewage"
|
||||
}
|
||||
],
|
||||
urls: {"program_listing_url": "/dashboard/programs/", "commerce_api_url": "/api/commerce/v0/baskets/", "track_selection_url": "/course_modes/choose/", "program_record_url": "/foo/bar", "buy_subscription_url": "/subscriptions", "orders_and_subscriptions_url": "/orders", "subscriptions_learner_help_center_url": "/learner"},
|
||||
urls: {"program_listing_url": "/dashboard/programs/", "commerce_api_url": "/api/commerce/v0/baskets/", "track_selection_url": "/course_modes/choose/"},
|
||||
userPreferences: {"pref-lang": "en"}
|
||||
};
|
||||
/* eslint-enable */
|
||||
let programModel;
|
||||
let courseData;
|
||||
let subscriptionData;
|
||||
let certificateCollection;
|
||||
let isSubscriptionEligible;
|
||||
|
||||
const testCircle = (progress) => {
|
||||
const $circle = view.$('.progress-circle');
|
||||
@@ -53,55 +49,15 @@ describe('Program Progress View', () => {
|
||||
expect(parseInt($numbers.find('.total').html(), 10)).toEqual(total);
|
||||
};
|
||||
|
||||
const testSubscriptionState = (state, heading, body) => {
|
||||
isSubscriptionEligible = true;
|
||||
subscriptionData.subscription_state = state;
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
view = initView();
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
body += ' on the <a class="subscription-link" href="/orders">Orders and subscriptions</a> page';
|
||||
|
||||
expect(view.$('.js-subscription-info')[0]).toBeInDOM();
|
||||
expect(
|
||||
view.$('.js-subscription-info .divider-heading').text().trim(),
|
||||
).toEqual(heading);
|
||||
expect(
|
||||
view.$('.js-subscription-info .subscription-section p:nth-child(1)'),
|
||||
).toContainHtml(body);
|
||||
expect(
|
||||
view.$('.js-subscription-info .subscription-section p:nth-child(2)'),
|
||||
).toContainText(
|
||||
/Need help\? Check out the.*Learner Help Center.*to troubleshoot issues or contact support/,
|
||||
);
|
||||
expect(
|
||||
view.$('.js-subscription-info .subscription-section p:nth-child(2) .subscription-link').attr('href'),
|
||||
).toEqual('/learner');
|
||||
};
|
||||
|
||||
const initView = () => new ProgramSidebarView({
|
||||
el: '.js-program-sidebar',
|
||||
model: programModel,
|
||||
courseModel: courseData,
|
||||
subscriptionModel: new SubscriptionModel({
|
||||
context: {
|
||||
programData: {
|
||||
subscription_eligible: isSubscriptionEligible,
|
||||
subscription_prices: [{
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
}],
|
||||
},
|
||||
subscriptionData: [subscriptionData],
|
||||
urls: data.urls,
|
||||
userPreferences: data.userPreferences,
|
||||
},
|
||||
}),
|
||||
certificateCollection,
|
||||
industryPathways: data.industryPathways,
|
||||
creditPathways: data.creditPathways,
|
||||
programTabViewEnabled: false,
|
||||
urls: data.urls,
|
||||
isSubscriptionEligible,
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -109,14 +65,6 @@ describe('Program Progress View', () => {
|
||||
programModel = new Backbone.Model(data.programData);
|
||||
courseData = new Backbone.Model(data.courseData);
|
||||
certificateCollection = new Backbone.Collection(data.certificateData);
|
||||
isSubscriptionEligible = false;
|
||||
subscriptionData = {
|
||||
trial_end: '1970-01-01T03:25:45Z',
|
||||
current_period_end: '1970-06-03T07:12:04Z',
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
subscription_state: 'pre',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -203,69 +151,14 @@ describe('Program Progress View', () => {
|
||||
el: '.js-program-sidebar',
|
||||
model: programModel,
|
||||
courseModel: courseData,
|
||||
subscriptionModel: new SubscriptionModel({
|
||||
context: {
|
||||
programData: {
|
||||
subscription_eligible: isSubscriptionEligible,
|
||||
subscription_prices: [{
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
}],
|
||||
},
|
||||
subscriptionData: [subscriptionData],
|
||||
urls: data.urls,
|
||||
userPreferences: data.userPreferences,
|
||||
},
|
||||
}),
|
||||
certificateCollection,
|
||||
industryPathways: [],
|
||||
creditPathways: [],
|
||||
programTabViewEnabled: false,
|
||||
urls: data.urls,
|
||||
isSubscriptionEligible,
|
||||
});
|
||||
|
||||
expect(emptyView.$('.program-credit-pathways .divider-heading')).toHaveLength(0);
|
||||
expect(emptyView.$('.program-industry-pathways .divider-heading')).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not render subscription info if program is not subscription eligible', () => {
|
||||
view = initView();
|
||||
expect(view.$('.js-subscription-info')[0]).not.toBeInDOM();
|
||||
});
|
||||
|
||||
it('should render subscription info if program is subscription eligible', () => {
|
||||
testSubscriptionState(
|
||||
'pre',
|
||||
'Inactive subscription',
|
||||
'If you had a subscription previously, your payment history is still available',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render active trial subscription info if subscription is active with trial', () => {
|
||||
subscriptionData.trial_end = moment().add(3, 'days').utc().format(
|
||||
'YYYY-MM-DDTHH:mm:ss[Z]',
|
||||
);
|
||||
testSubscriptionState(
|
||||
'active',
|
||||
'Trial subscription',
|
||||
'View your receipts or modify your subscription',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render active subscription info if subscription active', () => {
|
||||
testSubscriptionState(
|
||||
'active',
|
||||
'Active subscription',
|
||||
'View your receipts or modify your subscription',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render inactive subscription info if subscription inactive', () => {
|
||||
testSubscriptionState(
|
||||
'inactive',
|
||||
'Inactive subscription',
|
||||
'Restart your subscription for $100/month USD. Your payment history is still available',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,11 +7,6 @@ describe('Program Details View', () => {
|
||||
let view = null;
|
||||
const options = {
|
||||
programData: {
|
||||
subscription_eligible: false,
|
||||
subscription_prices: [{
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
}],
|
||||
subtitle: '',
|
||||
overview: '',
|
||||
weeks_to_complete: null,
|
||||
@@ -468,24 +463,11 @@ describe('Program Details View', () => {
|
||||
},
|
||||
],
|
||||
},
|
||||
subscriptionData: [
|
||||
{
|
||||
trial_end: '1970-01-01T03:25:45Z',
|
||||
current_period_end: '1970-06-03T07:12:04Z',
|
||||
price: '100.00',
|
||||
currency: 'USD',
|
||||
subscription_state: 'pre',
|
||||
},
|
||||
],
|
||||
urls: {
|
||||
program_listing_url: '/dashboard/programs/',
|
||||
commerce_api_url: '/api/commerce/v0/baskets/',
|
||||
track_selection_url: '/course_modes/choose/',
|
||||
program_record_url: 'http://credentials.example.com/records/programs/UUID',
|
||||
buy_subscription_url: '/subscriptions',
|
||||
manage_subscription_url: '/orders',
|
||||
subscriptions_learner_help_center_url: '/learner',
|
||||
orders_and_subscriptions_url: '/orders',
|
||||
},
|
||||
userPreferences: {
|
||||
'pref-lang': 'en',
|
||||
@@ -513,59 +495,9 @@ describe('Program Details View', () => {
|
||||
},
|
||||
],
|
||||
programTabViewEnabled: false,
|
||||
isUserB2CSubscriptionsEnabled: false,
|
||||
};
|
||||
const data = options.programData;
|
||||
|
||||
const testSubscriptionState = (state, heading, body, trial = false) => {
|
||||
const subscriptionData = {
|
||||
...options.subscriptionData[0],
|
||||
subscription_state: state,
|
||||
};
|
||||
if (trial) {
|
||||
subscriptionData.trial_end = moment().add(3, 'days').utc().format(
|
||||
'YYYY-MM-DDTHH:mm:ss[Z]',
|
||||
);
|
||||
}
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
view = initView({
|
||||
// eslint-disable-next-line no-undef
|
||||
programData: $.extend({}, options.programData, {
|
||||
subscription_eligible: true,
|
||||
}),
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
subscriptionData: [subscriptionData],
|
||||
});
|
||||
view.render();
|
||||
expect(view.$('.upgrade-subscription')[0]).toBeInDOM();
|
||||
expect(view.$('.upgrade-subscription .upgrade-button'))
|
||||
.toContainText(heading);
|
||||
expect(view.$('.upgrade-subscription .subscription-info-brief'))
|
||||
.toContainText(body);
|
||||
};
|
||||
|
||||
const testSubscriptionSunsetting = (state, heading, body) => {
|
||||
const subscriptionData = {
|
||||
...options.subscriptionData[0],
|
||||
subscription_state: state,
|
||||
};
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
view = initView({
|
||||
// eslint-disable-next-line no-undef
|
||||
programData: $.extend({}, options.programData, {
|
||||
subscription_eligible: false,
|
||||
}),
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
subscriptionData: [subscriptionData],
|
||||
});
|
||||
view.render();
|
||||
expect(view.$('.upgrade-subscription')[0]).not.toBeInDOM();
|
||||
expect(view.$('.upgrade-subscription .upgrade-button')).not
|
||||
.toContainText(heading);
|
||||
expect(view.$('.upgrade-subscription .subscription-info-brief')).not
|
||||
.toContainText(body);
|
||||
};
|
||||
|
||||
const initView = (updates) => {
|
||||
// eslint-disable-next-line no-undef
|
||||
const viewOptions = $.extend({}, options, updates);
|
||||
@@ -730,37 +662,4 @@ describe('Program Details View', () => {
|
||||
properties,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render the get subscription link if program is not active', () => {
|
||||
testSubscriptionSunsetting(
|
||||
'pre',
|
||||
'Start 7-day free trial',
|
||||
'$100/month USD subscription after trial ends. Cancel anytime.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render appropriate subscription text when subscription is active with trial', () => {
|
||||
testSubscriptionSunsetting(
|
||||
'active',
|
||||
'Manage my subscription',
|
||||
'Trial ends',
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render appropriate subscription text when subscription is active', () => {
|
||||
testSubscriptionSunsetting(
|
||||
'active',
|
||||
'Manage my subscription',
|
||||
'Your next billing date is',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render appropriate subscription text when subscription is inactive', () => {
|
||||
testSubscriptionSunsetting(
|
||||
'inactive',
|
||||
'Restart my subscription',
|
||||
'$100/month USD subscription. Cancel anytime.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,27 +13,14 @@ describe('Program List Header View', () => {
|
||||
{
|
||||
uuid: '5b234e3c-3a2e-472e-90db-6f51501dc86c',
|
||||
title: 'edX Demonstration Program',
|
||||
subscription_eligible: null,
|
||||
subscription_prices: [],
|
||||
detail_url: '/dashboard/programs/5b234e3c-3a2e-472e-90db-6f51501dc86c/',
|
||||
},
|
||||
{
|
||||
uuid: 'b90d70d5-f981-4508-bdeb-5b792d930c03',
|
||||
title: 'Test Program',
|
||||
subscription_eligible: true,
|
||||
subscription_prices: [{ price: '500.00', currency: 'USD' }],
|
||||
detail_url: '/dashboard/programs/b90d70d5-f981-4508-bdeb-5b792d930c03/',
|
||||
},
|
||||
],
|
||||
programsSubscriptionData: [
|
||||
{
|
||||
id: 'eeb25640-9741-4c11-963c-8a27337f217c',
|
||||
resource_id: 'b90d70d5-f981-4508-bdeb-5b792d930c03',
|
||||
trial_end: '2022-04-20T05:59:42Z',
|
||||
current_period_end: '2023-05-08T05:59:42Z',
|
||||
subscription_state: 'active',
|
||||
},
|
||||
],
|
||||
userProgress: [
|
||||
{
|
||||
uuid: '5b234e3c-3a2e-472e-90db-6f51501dc86c',
|
||||
@@ -50,13 +37,9 @@ describe('Program List Header View', () => {
|
||||
all_unenrolled: true,
|
||||
},
|
||||
],
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
context.subscriptionCollection = new Backbone.Collection(
|
||||
context.programsSubscriptionData,
|
||||
);
|
||||
context.progressCollection = new ProgressCollection(
|
||||
context.userProgress,
|
||||
);
|
||||
@@ -78,18 +61,4 @@ describe('Program List Header View', () => {
|
||||
it('should render the program heading', () => {
|
||||
expect(view.$('h2:first').text().trim()).toEqual('My programs');
|
||||
});
|
||||
|
||||
it('should render a program alert', () => {
|
||||
expect(
|
||||
view.$('.js-program-list-alerts .alert .alert-heading').html().trim(),
|
||||
).toEqual('Enroll in a Test Program\'s course');
|
||||
expect(
|
||||
view.$('.js-program-list-alerts .alert .alert-message'),
|
||||
).toContainHtml(
|
||||
'According to our records, you are not enrolled in any courses included in your Test Program program subscription. Enroll in a course from the <i>Program Details</i> page.',
|
||||
);
|
||||
expect(
|
||||
view.$('.js-program-list-alerts .alert .view-button').attr('href'),
|
||||
).toEqual('/dashboard/programs/b90d70d5-f981-4508-bdeb-5b792d930c03/');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,12 +6,6 @@ describe('Sidebar View', () => {
|
||||
let view = null;
|
||||
const context = {
|
||||
marketingUrl: 'https://www.example.org/programs',
|
||||
subscriptionUpsellData: {
|
||||
marketing_url: 'https://www.example.org/program-subscriptions',
|
||||
minimum_price: '$39',
|
||||
trial_length: 7,
|
||||
},
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -32,10 +26,6 @@ describe('Sidebar View', () => {
|
||||
expect(view).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not render the subscription upsell section', () => {
|
||||
expect(view.$('.js-subscription-upsell')[0]).not.toBeInDOM();
|
||||
});
|
||||
|
||||
it('should load the exploration panel given a marketing URL', () => {
|
||||
expect(view.$('.program-advertise .advertise-message').html().trim())
|
||||
.toEqual(
|
||||
@@ -49,10 +39,6 @@ describe('Sidebar View', () => {
|
||||
view.remove();
|
||||
view = new SidebarView({
|
||||
el: '.sidebar',
|
||||
context: {
|
||||
isUserB2CSubscriptionsEnabled: true,
|
||||
subscriptionUpsellData: context.subscriptionUpsellData,
|
||||
},
|
||||
});
|
||||
view.render();
|
||||
const $ad = view.$el.find('.program-advertise');
|
||||
|
||||
@@ -9,8 +9,6 @@ import ExpiredNotificationView from './expired_notification_view';
|
||||
import CourseEnrollView from './course_enroll_view';
|
||||
import EntitlementView from './course_entitlement_view';
|
||||
|
||||
import SubscriptionModel from '../models/program_subscription_model';
|
||||
|
||||
import pageTpl from '../../../templates/learner_dashboard/course_card.underscore';
|
||||
|
||||
class CourseCardView extends Backbone.View {
|
||||
@@ -27,9 +25,6 @@ class CourseCardView extends Backbone.View {
|
||||
this.enrollModel = new EnrollModel();
|
||||
if (options.context) {
|
||||
this.urlModel = new Backbone.Model(options.context.urls);
|
||||
this.subscriptionModel = new SubscriptionModel({
|
||||
context: options.context,
|
||||
});
|
||||
this.enrollModel.urlRoot = this.urlModel.get('commerce_api_url');
|
||||
}
|
||||
this.context = options.context || {};
|
||||
@@ -93,8 +88,6 @@ class CourseCardView extends Backbone.View {
|
||||
this.upgradeMessage = new UpgradeMessageView({
|
||||
$el: $upgradeMessage,
|
||||
model: this.model,
|
||||
subscriptionModel: this.subscriptionModel,
|
||||
isSubscriptionEligible: this.context.isSubscriptionEligible,
|
||||
});
|
||||
|
||||
$certStatus.remove();
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
import Backbone from 'backbone';
|
||||
|
||||
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
|
||||
import StringUtils from 'edx-ui-toolkit/js/utils/string-utils';
|
||||
|
||||
import warningIcon from '../../../images/warning-icon.svg';
|
||||
import programAlertTpl from '../../../templates/learner_dashboard/program_alert_list_view.underscore';
|
||||
|
||||
class ProgramAlertListView extends Backbone.View {
|
||||
constructor(options) {
|
||||
const defaults = {
|
||||
el: '.js-program-details-alerts',
|
||||
};
|
||||
// eslint-disable-next-line prefer-object-spread
|
||||
super(Object.assign({}, defaults, options));
|
||||
}
|
||||
|
||||
initialize({ context }) {
|
||||
this.tpl = HtmlUtils.template(programAlertTpl);
|
||||
this.enrollmentAlerts = context.enrollmentAlerts || [];
|
||||
this.trialEndingAlerts = context.trialEndingAlerts || [];
|
||||
this.pageType = context.pageType;
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = {
|
||||
alertList: this.getAlertList(),
|
||||
warningIcon,
|
||||
};
|
||||
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
||||
}
|
||||
|
||||
getAlertList() {
|
||||
const alertList = this.enrollmentAlerts.map(
|
||||
({ title: programName, url }) => ({
|
||||
url,
|
||||
// eslint-disable-next-line no-undef
|
||||
urlText: gettext('View program'),
|
||||
title: StringUtils.interpolate(
|
||||
// eslint-disable-next-line no-undef
|
||||
gettext('Enroll in a {programName}\'s course'),
|
||||
{ programName },
|
||||
),
|
||||
message: this.pageType === 'programDetails'
|
||||
? StringUtils.interpolate(
|
||||
// eslint-disable-next-line no-undef
|
||||
gettext('You have an active subscription to the {programName} program but are not enrolled in any courses. Enroll in a remaining course and enjoy verified access.'),
|
||||
{ programName },
|
||||
)
|
||||
: HtmlUtils.interpolateHtml(
|
||||
// eslint-disable-next-line no-undef
|
||||
gettext('According to our records, you are not enrolled in any courses included in your {programName} program subscription. Enroll in a course from the {i_start}Program Details{i_end} page.'),
|
||||
{
|
||||
programName,
|
||||
i_start: HtmlUtils.HTML('<i>'),
|
||||
i_end: HtmlUtils.HTML('</i>'),
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
return alertList.concat(this.trialEndingAlerts.map(
|
||||
({ title: programName, remainingDays, ...data }) => ({
|
||||
title: StringUtils.interpolate(
|
||||
remainingDays < 1
|
||||
// eslint-disable-next-line no-undef
|
||||
? gettext('Subscription trial expires in less than 24 hours')
|
||||
// eslint-disable-next-line no-undef
|
||||
: ngettext('Subscription trial expires in {remainingDays} day', 'Subscription trial expires in {remainingDays} days', remainingDays),
|
||||
{ remainingDays },
|
||||
),
|
||||
message: StringUtils.interpolate(
|
||||
remainingDays < 1
|
||||
// eslint-disable-next-line no-undef
|
||||
? gettext('Your {programName} trial will expire at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.')
|
||||
// eslint-disable-next-line no-undef
|
||||
: ngettext('Your {programName} trial will expire in {remainingDays} day at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.', 'Your {programName} trial will expire in {remainingDays} days at {trialEndTime} on {trialEndDate} and the card on file will be charged {subscriptionPrice}.', remainingDays),
|
||||
{
|
||||
programName,
|
||||
remainingDays,
|
||||
...data,
|
||||
},
|
||||
),
|
||||
}),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
export default ProgramAlertListView;
|
||||
@@ -30,10 +30,6 @@ class ProgramCardView extends Backbone.View {
|
||||
uuid: this.model.get('uuid'),
|
||||
});
|
||||
}
|
||||
this.isSubscribed = (
|
||||
context.isUserB2CSubscriptionsEnabled &&
|
||||
this.model.get('subscriptionIndex') > -1
|
||||
) ?? false;
|
||||
this.render();
|
||||
}
|
||||
|
||||
@@ -45,7 +41,6 @@ class ProgramCardView extends Backbone.View {
|
||||
this.getProgramProgress(),
|
||||
{
|
||||
orgList: orgList.join(' '),
|
||||
isSubscribed: this.isSubscribed,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
@@ -30,9 +30,7 @@ class ProgramDetailsSidebarView extends Backbone.View {
|
||||
this.industryPathways = options.industryPathways;
|
||||
this.creditPathways = options.creditPathways;
|
||||
this.programModel = options.model;
|
||||
this.subscriptionModel = options.subscriptionModel;
|
||||
this.programTabViewEnabled = options.programTabViewEnabled;
|
||||
this.isSubscriptionEligible = options.isSubscriptionEligible;
|
||||
this.urls = options.urls;
|
||||
this.render();
|
||||
}
|
||||
@@ -42,14 +40,12 @@ class ProgramDetailsSidebarView extends Backbone.View {
|
||||
const data = $.extend(
|
||||
{},
|
||||
this.model.toJSON(),
|
||||
this.subscriptionModel.toJSON(),
|
||||
{
|
||||
programCertificate: this.programCertificate
|
||||
? this.programCertificate.toJSON() : {},
|
||||
industryPathways: this.industryPathways,
|
||||
creditPathways: this.creditPathways,
|
||||
programTabViewEnabled: this.programTabViewEnabled,
|
||||
isSubscriptionEligible: this.isSubscriptionEligible,
|
||||
arrowUprightIcon,
|
||||
...this.urls,
|
||||
},
|
||||
|
||||
@@ -10,10 +10,6 @@ import CourseCardView from './course_card_view';
|
||||
// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
|
||||
import HeaderView from './program_header_view';
|
||||
import SidebarView from './program_details_sidebar_view';
|
||||
import AlertListView from './program_alert_list_view';
|
||||
|
||||
// eslint-disable-next-line import/no-named-as-default, import/no-named-as-default-member
|
||||
import SubscriptionModel from '../models/program_subscription_model';
|
||||
|
||||
import launchIcon from '../../../images/launch-icon.svg';
|
||||
import restartIcon from '../../../images/restart-icon.svg';
|
||||
@@ -27,7 +23,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
el: '.js-program-details-wrapper',
|
||||
events: {
|
||||
'click .complete-program': 'trackPurchase',
|
||||
'click .js-subscription-cta': 'trackSubscriptionCTA',
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line prefer-object-spread
|
||||
@@ -46,9 +41,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
this.certificateCollection = new Backbone.Collection(
|
||||
this.options.certificateData,
|
||||
);
|
||||
this.subscriptionModel = new SubscriptionModel({
|
||||
context: this.options,
|
||||
});
|
||||
this.completedCourseCollection = new CourseCardCollection(
|
||||
this.courseData.get('completed') || [],
|
||||
this.options.userPreferences,
|
||||
@@ -61,11 +53,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
this.courseData.get('not_started') || [],
|
||||
this.options.userPreferences,
|
||||
);
|
||||
this.subscriptionEventParams = {
|
||||
label: this.options.programData.title,
|
||||
program_uuid: this.options.programData.uuid,
|
||||
};
|
||||
this.options.isSubscriptionEligible = this.getIsSubscriptionEligible();
|
||||
|
||||
this.render();
|
||||
|
||||
@@ -76,7 +63,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
pageName: 'program_dashboard',
|
||||
linkCategory: 'green_upgrade',
|
||||
});
|
||||
this.trackSubscriptionEligibleProgramView();
|
||||
}
|
||||
|
||||
static getUrl(base, programData) {
|
||||
@@ -107,7 +93,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
creditPathways: this.options.creditPathways,
|
||||
discussionFragment: this.options.discussionFragment,
|
||||
live_fragment: this.options.live_fragment,
|
||||
isSubscriptionEligible: this.options.isSubscriptionEligible,
|
||||
launchIcon,
|
||||
restartIcon,
|
||||
};
|
||||
@@ -115,7 +100,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
data = $.extend(
|
||||
data,
|
||||
this.programModel.toJSON(),
|
||||
this.subscriptionModel.toJSON(),
|
||||
);
|
||||
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
||||
this.postRender();
|
||||
@@ -126,20 +110,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
model: new Backbone.Model(this.options),
|
||||
});
|
||||
|
||||
if (this.options.isSubscriptionEligible) {
|
||||
const { enrollmentAlerts, trialEndingAlerts } = this.getAlerts();
|
||||
|
||||
if (enrollmentAlerts.length || trialEndingAlerts.length) {
|
||||
this.alertListView = new AlertListView({
|
||||
context: {
|
||||
enrollmentAlerts,
|
||||
trialEndingAlerts,
|
||||
pageType: 'programDetails',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.remainingCourseCollection.length > 0) {
|
||||
new CollectionListView({
|
||||
el: '.js-course-list-remaining',
|
||||
@@ -178,12 +148,10 @@ class ProgramDetailsView extends Backbone.View {
|
||||
el: '.js-program-sidebar',
|
||||
model: this.programModel,
|
||||
courseModel: this.courseData,
|
||||
subscriptionModel: this.subscriptionModel,
|
||||
certificateCollection: this.certificateCollection,
|
||||
industryPathways: this.options.industryPathways,
|
||||
creditPathways: this.options.creditPathways,
|
||||
programTabViewEnabled: this.options.programTabViewEnabled,
|
||||
isSubscriptionEligible: this.options.isSubscriptionEligible,
|
||||
urls: this.options.urls,
|
||||
});
|
||||
let hasIframe = false;
|
||||
@@ -197,59 +165,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
}).bind(this);
|
||||
}
|
||||
|
||||
getIsSubscriptionEligible() {
|
||||
const courseCollections = [
|
||||
this.completedCourseCollection,
|
||||
this.inProgressCourseCollection,
|
||||
];
|
||||
const isSomeCoursePurchasable = courseCollections.some((collection) => (
|
||||
collection.some((course) => (
|
||||
course.get('upgrade_url')
|
||||
&& !(course.get('expired') === true)
|
||||
))
|
||||
));
|
||||
const programPurchasedWithoutSubscription = (
|
||||
this.subscriptionModel.get('subscriptionState') !== 'active'
|
||||
&& this.subscriptionModel.get('subscriptionState') !== 'inactive'
|
||||
&& !isSomeCoursePurchasable
|
||||
&& this.remainingCourseCollection.length === 0
|
||||
);
|
||||
|
||||
const isSubscriptionActiveSunsetting = (
|
||||
this.subscriptionModel.get('subscriptionState') === 'active'
|
||||
)
|
||||
|
||||
return (
|
||||
this.options.isUserB2CSubscriptionsEnabled
|
||||
&& isSubscriptionActiveSunsetting
|
||||
&& !programPurchasedWithoutSubscription
|
||||
);
|
||||
}
|
||||
|
||||
getAlerts() {
|
||||
const alerts = {
|
||||
enrollmentAlerts: [],
|
||||
trialEndingAlerts: [],
|
||||
};
|
||||
if (this.subscriptionModel.get('subscriptionState') === 'active') {
|
||||
if (this.courseData.get('all_unenrolled')) {
|
||||
alerts.enrollmentAlerts.push({
|
||||
title: this.programModel.get('title'),
|
||||
});
|
||||
}
|
||||
if (
|
||||
this.subscriptionModel.get('remainingDays') <= 7
|
||||
&& this.subscriptionModel.get('hasActiveTrial')
|
||||
) {
|
||||
alerts.trialEndingAlerts.push({
|
||||
title: this.programModel.get('title'),
|
||||
...this.subscriptionModel.toJSON(),
|
||||
});
|
||||
}
|
||||
}
|
||||
return alerts;
|
||||
}
|
||||
|
||||
trackPurchase() {
|
||||
const data = this.options.programData;
|
||||
window.analytics.track('edx.bi.user.dashboard.program.purchase', {
|
||||
@@ -258,37 +173,6 @@ class ProgramDetailsView extends Backbone.View {
|
||||
uuid: data.uuid,
|
||||
});
|
||||
}
|
||||
|
||||
trackSubscriptionCTA() {
|
||||
const state = this.subscriptionModel.get('subscriptionState');
|
||||
|
||||
if (state === 'active') {
|
||||
window.analytics.track(
|
||||
'edx.bi.user.subscription.program-detail-page.manage.clicked',
|
||||
this.subscriptionEventParams,
|
||||
);
|
||||
} else {
|
||||
const isNewSubscription = state !== 'inactive';
|
||||
window.analytics.track(
|
||||
'edx.bi.user.subscription.program-detail-page.subscribe.clicked',
|
||||
{
|
||||
category: `${this.options.programData.variant} bundle`,
|
||||
is_new_subscription: isNewSubscription,
|
||||
is_trial_eligible: isNewSubscription,
|
||||
...this.subscriptionEventParams,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
trackSubscriptionEligibleProgramView() {
|
||||
if (this.options.isSubscriptionEligible) {
|
||||
window.analytics.track(
|
||||
'edx.bi.user.subscription.program-detail-page.viewed',
|
||||
this.subscriptionEventParams,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ProgramDetailsView;
|
||||
|
||||
@@ -42,22 +42,11 @@ class ProgramHeaderView extends Backbone.View {
|
||||
return logo;
|
||||
}
|
||||
|
||||
getIsSubscribed() {
|
||||
const isSubscriptionEligible = this.model.get('isSubscriptionEligible');
|
||||
const subscriptionData = this.model.get('subscriptionData')?.[0];
|
||||
|
||||
return (
|
||||
isSubscriptionEligible &&
|
||||
subscriptionData?.subscription_state === 'active'
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
// eslint-disable-next-line no-undef
|
||||
const data = $.extend(this.model.toJSON(), {
|
||||
breakpoints: this.breakpoints,
|
||||
logo: this.getLogo(),
|
||||
isSubscribed: this.getIsSubscribed(),
|
||||
});
|
||||
|
||||
if (this.model.get('programData')) {
|
||||
|
||||
@@ -2,10 +2,6 @@ import Backbone from 'backbone';
|
||||
|
||||
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
|
||||
|
||||
import AlertListView from './program_alert_list_view';
|
||||
|
||||
import SubscriptionModel from '../models/program_subscription_model';
|
||||
|
||||
import programListHeaderTpl from '../../../templates/learner_dashboard/program_list_header_view.underscore';
|
||||
|
||||
class ProgramListHeaderView extends Backbone.View {
|
||||
@@ -19,76 +15,11 @@ class ProgramListHeaderView extends Backbone.View {
|
||||
initialize({ context }) {
|
||||
this.context = context;
|
||||
this.tpl = HtmlUtils.template(programListHeaderTpl);
|
||||
this.programAndSubscriptionData = context.programsData
|
||||
.map((programData) => ({
|
||||
programData,
|
||||
subscriptionData: context.subscriptionCollection
|
||||
?.findWhere({
|
||||
resource_id: programData.uuid,
|
||||
subscription_state: 'active',
|
||||
})
|
||||
?.toJSON(),
|
||||
}))
|
||||
.filter(({ subscriptionData }) => !!subscriptionData);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
HtmlUtils.setHtml(this.$el, this.tpl(this.context));
|
||||
this.postRender();
|
||||
}
|
||||
|
||||
postRender() {
|
||||
if (this.context.isUserB2CSubscriptionsEnabled) {
|
||||
const enrollmentAlerts = this.getEnrollmentAlerts();
|
||||
const trialEndingAlerts = this.getTrialEndingAlerts();
|
||||
|
||||
if (enrollmentAlerts.length || trialEndingAlerts.length) {
|
||||
this.alertListView = new AlertListView({
|
||||
el: '.js-program-list-alerts',
|
||||
context: {
|
||||
enrollmentAlerts,
|
||||
trialEndingAlerts,
|
||||
pageType: 'programList',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getEnrollmentAlerts() {
|
||||
return this.programAndSubscriptionData
|
||||
.map(({ programData, subscriptionData }) =>
|
||||
this.context.progressCollection?.findWhere({
|
||||
uuid: programData.uuid,
|
||||
all_unenrolled: true,
|
||||
}) ? {
|
||||
title: programData.title,
|
||||
url: programData.detail_url,
|
||||
} : null
|
||||
)
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
getTrialEndingAlerts() {
|
||||
return this.programAndSubscriptionData
|
||||
.map(({ programData, subscriptionData }) => {
|
||||
const subscriptionModel = new SubscriptionModel({
|
||||
context: {
|
||||
programData,
|
||||
subscriptionData: [subscriptionData],
|
||||
userPreferences: this.context?.userPreferences,
|
||||
},
|
||||
});
|
||||
return (
|
||||
subscriptionModel.get('remainingDays') <= 7 &&
|
||||
subscriptionModel.get('hasActiveTrial') && {
|
||||
title: programData.title,
|
||||
...subscriptionModel.toJSON(),
|
||||
}
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,6 @@ class SidebarView extends Backbone.View {
|
||||
constructor(options) {
|
||||
const defaults = {
|
||||
el: '.sidebar',
|
||||
events: {
|
||||
'click .js-subscription-upsell-cta ': 'trackSubscriptionUpsellCTA',
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line prefer-object-spread
|
||||
super(Object.assign({}, defaults, options));
|
||||
@@ -33,12 +30,6 @@ class SidebarView extends Backbone.View {
|
||||
context: this.context,
|
||||
});
|
||||
}
|
||||
|
||||
trackSubscriptionUpsellCTA() {
|
||||
window.analytics.track(
|
||||
'edx.bi.user.subscription.program-dashboard.upsell.clicked',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SidebarView;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import Backbone from 'backbone';
|
||||
|
||||
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
|
||||
|
||||
import subscriptionUpsellTpl from '../../../templates/learner_dashboard/subscription_upsell_view.underscore';
|
||||
|
||||
class SubscriptionUpsellView extends Backbone.View {
|
||||
constructor(options) {
|
||||
const defaults = {
|
||||
el: '.js-subscription-upsell',
|
||||
};
|
||||
// eslint-disable-next-line prefer-object-spread
|
||||
super(Object.assign({}, defaults, options));
|
||||
}
|
||||
|
||||
initialize(options) {
|
||||
this.tpl = HtmlUtils.template(subscriptionUpsellTpl);
|
||||
this.subscriptionUpsellModel = new Backbone.Model(
|
||||
options.subscriptionUpsellData,
|
||||
);
|
||||
this.render();
|
||||
}
|
||||
|
||||
render() {
|
||||
const data = this.subscriptionUpsellModel.toJSON();
|
||||
HtmlUtils.setHtml(this.$el, this.tpl(data));
|
||||
}
|
||||
}
|
||||
|
||||
export default SubscriptionUpsellView;
|
||||
@@ -3,18 +3,12 @@ import Backbone from 'backbone';
|
||||
import HtmlUtils from 'edx-ui-toolkit/js/utils/html-utils';
|
||||
|
||||
import upgradeMessageTpl from '../../../templates/learner_dashboard/upgrade_message.underscore';
|
||||
import upgradeMessageSubscriptionTpl from '../../../templates/learner_dashboard/upgrade_message_subscription.underscore';
|
||||
import trackECommerceEvents from '../../commerce/track_ecommerce_events';
|
||||
|
||||
class UpgradeMessageView extends Backbone.View {
|
||||
initialize(options) {
|
||||
if (options.isSubscriptionEligible) {
|
||||
this.messageTpl = HtmlUtils.template(upgradeMessageSubscriptionTpl);
|
||||
} else {
|
||||
this.messageTpl = HtmlUtils.template(upgradeMessageTpl);
|
||||
}
|
||||
this.messageTpl = HtmlUtils.template(upgradeMessageTpl);
|
||||
this.$el = options.$el;
|
||||
this.subscriptionModel = options.subscriptionModel;
|
||||
this.render();
|
||||
|
||||
const courseUpsellButtons = this.$el.find('.program_dashboard_course_upsell_button');
|
||||
@@ -30,7 +24,6 @@ class UpgradeMessageView extends Backbone.View {
|
||||
const data = $.extend(
|
||||
{},
|
||||
this.model.toJSON(),
|
||||
this.subscriptionModel.toJSON(),
|
||||
);
|
||||
HtmlUtils.setHtml(this.$el, this.messageTpl(data));
|
||||
}
|
||||
|
||||
@@ -90,21 +90,6 @@ $btn-color-primary: $primary-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.program-details-alerts {
|
||||
.page-banner {
|
||||
margin: 0;
|
||||
padding: 0 0 48px;
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.program-details-tab-alerts {
|
||||
.page-banner {
|
||||
margin: 0;
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
// CSS for April 2017 version of Program Details Page
|
||||
.program-details {
|
||||
.window-wrap {
|
||||
@@ -449,42 +434,6 @@ $btn-color-primary: $primary-dark;
|
||||
}
|
||||
}
|
||||
|
||||
.upgrade-subscription {
|
||||
margin: 16px 0 10px;
|
||||
row-gap: 16px;
|
||||
column-gap: 24px;
|
||||
}
|
||||
|
||||
.subscription-icon-launch {
|
||||
width: 22.5px;
|
||||
height: 22.5px;
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
|
||||
.subscription-icon-restart {
|
||||
width: 22.5px;
|
||||
height: 22.5px;
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
|
||||
.subscription-icon-arrow-upright {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
|
||||
.subscription-info-brief {
|
||||
font-size: 0.9375em;
|
||||
color: $gray-500;
|
||||
}
|
||||
|
||||
.subscription-info-upsell {
|
||||
margin-top: 0.25rem;
|
||||
font-size: 0.8125em;
|
||||
}
|
||||
|
||||
.program-course-card {
|
||||
width: 100%;
|
||||
padding: 15px 15px 15px 0px;
|
||||
@@ -681,24 +630,6 @@ $btn-color-primary: $primary-dark;
|
||||
.program-sidebar {
|
||||
padding: 40px 40px 40px 0px;
|
||||
|
||||
.program-record,.subscription-info {
|
||||
text-align: left;
|
||||
padding-bottom: 2em;
|
||||
}
|
||||
|
||||
.subscription-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
color: #414141;
|
||||
|
||||
.subscription-link {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border-bottom: 1px solid currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-section {
|
||||
font-size: 0.9375em;
|
||||
width: auto;
|
||||
|
||||
@@ -39,13 +39,6 @@
|
||||
.program-cards-container {
|
||||
@include grid-container();
|
||||
padding-top: 32px;
|
||||
|
||||
.subscription-badge {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
||||
Reference in New Issue
Block a user