static expiration box

This commit is contained in:
Emma Green
2021-03-05 10:41:49 -05:00
parent 45a68973b7
commit 1f7b875ea6
3 changed files with 305 additions and 38 deletions

View File

@@ -201,6 +201,7 @@ export async function getOutlineTabData(courseId) {
resumeCourse,
verifiedMode,
welcomeMessageHtml,
timeOffsetMillis: 0,
};
}

View File

@@ -1,22 +1,223 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { FormattedDate, FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import messages from '../messages';
import { useModel } from '../../../generic/model-store';
import { UpgradeButton } from '../../../generic/upgrade-button';
import VerifiedCert from '../../../generic/assets/edX_certificate.png';
function UpgradeCard({ courseId, intl, onLearnMore }) {
function UpsellNoFBECardContent(){
return (
<ul className="fa-ul">
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.verifiedCertLink"
defaultMessage="Earn a {verifiedCertLink} of completion to showcase on your resume"
values={{
verifiedCertLink: (
<a className="inline-link-underline" rel="noopener noreferrer" target="_blank" href={`${getConfig().MARKETING_SITE_BASE_URL}/verified-certificate`}>verified certificate</a>
),
}}
/>
</li>
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.nonProfitMission"
defaultMessage="Support our {nonProfitMission} at edX"
values={{
nonProfitMission: (
<span className="font-weight-bold">non-profit mission</span>
),
}}
/>
</li>
</ul>
);
}
function UpsellFBEFarAwayCardContent() {
return (
<ul className="fa-ul">
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.verifiedCertLink"
defaultMessage="Earn a {verifiedCertLink} of completion to showcase on your resume"
values={{
verifiedCertLink: (
<a className="inline-link-underline" rel="noopener noreferrer" target="_blank" href={`${getConfig().MARKETING_SITE_BASE_URL}/verified-certificate`}>verified certificate</a>
),
}}
/>
</li>
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.unlock-graded"
defaultMessage="Unlock your access to all course activities, including {gradedAssignments}"
values={{
gradedAssignments: (
<span className="font-weight-bold">graded assignments</span>
),
}}
/>
</li>
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.fullAccess"
defaultMessage="{fullAccess} to course content and materials, even after the course ends"
values={{
fullAccess: (
<span className="font-weight-bold">Full access</span>
),
}}
/>
</li>
<li>
<span className="fa-li"><FontAwesomeIcon icon={faCheck} /></span>
<FormattedMessage
id="learning.outline.alert.upgradecard.nonProfitMission"
defaultMessage="Support our {nonProfitMission} at edX"
values={{
nonProfitMission: (
<span className="font-weight-bold">non-profit mission</span>
),
}}
/>
</li>
</ul>
);
}
function UpsellFBESoonCardContent({accessExpirationDate, timezoneFormatArgs}) {
return (
<div>
<p>
<FormattedMessage
id="learning.outline.alert.upgradecard.expirationAccessLoss"
defaultMessage="You will lose all access to this course, {includingAnyProgress}, on {date}."
values={{
includingAnyProgress: (<span className="font-weight-bold">including any progress</span>),
date: (<FormattedDate
key="accessDate"
day="numeric"
month="long"
value={new Date(accessExpirationDate)}
{...timezoneFormatArgs}
/>),
}}
/>
</p>
<p>
<FormattedMessage
id="learning.outline.alert.upgradecard.expirationVerifiedCert"
defaultMessage="Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the {benefitsOfUpgrading}."
values={{
benefitsOfUpgrading: ( <a className="inline-link-underline" rel="noopener noreferrer" target="_blank" href="https://support.edx.org/hc/en-us/articles/360013426573-What-are-the-differences-between-audit-free-and-verified-paid-courses-">benefits of upgrading</a>),
}}
/>
</p>
</div>
);
}
UpsellFBESoonCardContent.propTypes = {
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
timezoneFormatArgs: PropTypes.shape({
timeZone: PropTypes.string,
})
};
function ExpirationCountdown({hoursToExpiration}){
let expirationText;
console.log(hoursToExpiration)
if(hoursToExpiration >= 24){
expirationText =<FormattedMessage
id="learning.outline.alert.upgradecard.expiration.days"
defaultMessage={`{dayCount, number} {dayCount, plural,
one {day}
other {days}} left`}
values={{
dayCount: (Math.floor(hoursToExpiration/24)),
}}
/>
} else if (hoursToExpiration >= 1){
expirationText= <FormattedMessage
id="learning.outline.alert.upgradecard.expiration.hours"
defaultMessage={`{hourCount, number} {hourCount, plural,
one {hour}
other {hours}} left`}
values={{
hourCount: (hoursToExpiration),
}}
/>
} else {
expirationText =<FormattedMessage
id="learning.outline.alert.upgradecard.expiration.minutes"
defaultMessage="Less than 1 hour left"
/>
}
return(<div className="p-3 upsell-warning">{expirationText}</div>)
}
ExpirationCountdown.propTypes = {
hoursToExpiration: PropTypes.number.isRequired,
};
function AccessExpirationDateBanner({accessExpirationDate, timezoneFormatArgs}){
return (
<div className="p-3 upsell-warning-light">
<FormattedMessage
id="learning.outline.alert.upgradecard.expirationr"
defaultMessage="Course access will expire {date}"
values={{
date: (<FormattedDate
key="accessExpireDate"
day="numeric"
month="long"
value={accessExpirationDate}
{...timezoneFormatArgs}
/>),
}}
/>
</div>
);
}
AccessExpirationDateBanner.propTypes = {
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
timezoneFormatArgs: PropTypes.shape({
timeZone: PropTypes.string,
})
};
function UpgradeCard({ courseId }) {
const { org } = useModel('courseHomeMeta', courseId);
const {
offer,
verifiedMode,
accessExpiration,
datesBannerInfo: {
contentTypeGatingEnabled,
},
datesWidget: {
userTimezone,
},
timeOffsetMillis,
} = useModel('outline', courseId);
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
const correctedTime = new Date(Date.now() + timeOffsetMillis);
if (!verifiedMode) {
return null;
}
@@ -55,41 +256,90 @@ function UpgradeCard({ courseId, intl, onLearnMore }) {
});
};
return (
<section className="mb-4 p-3 outline-sidebar-upgrade-card">
<h2 className="h4" id="outline-sidebar-upgrade-header">{intl.formatMessage(messages.upgradeTitle)}</h2>
<div className="row w-100 m-0">
<div className="col-6 col-md-12 col-lg-3 col-xl-4 p-0 text-md-center text-lg-left">
<img
alt={intl.formatMessage(messages.certAlt)}
className="w-100"
src={VerifiedCert}
style={{ maxWidth: '10rem' }}
/*
There are 4 parts that change in the upgrade card:
upgradeCardHeaderText
expirationBanner
upsellMessage
offerCode
*/
let upgradeCardHeaderText;
let expirationBanner;
let upsellMessage;
let offerCode;
if(!!accessExpiration && !!contentTypeGatingEnabled){
if(!!offer){ // if there's a first purchase discount, show it
const hoursToDiscountExpiration = Math.floor((new Date(offer.expirationDate) - correctedTime) /1000/ 60 /60)
upgradeCardHeaderText = <FormattedMessage
id="learning.outline.alert.upgradecard.firstTimeLearnerDiscount"
defaultMessage="{percentage}% First-Time Learner Discount"
values={{
percentage: (offer.percentage),
}}
/>
expirationBanner = <ExpirationCountdown hoursToExpiration={hoursToDiscountExpiration}/>
upsellMessage = <UpsellFBEFarAwayCardContent />
offerCode = <div className="bg-light p-3 text-center discount-info">
<FormattedMessage
id="learning.outline.alert.upgradecard.code"
defaultMessage="Use code {code} at checkout"
values={{
code: (<span className="font-weight-bold">{offer.code}</span>),
}}
/>
</div>
} else {
const accessExpirationDate = new Date(accessExpiration.expirationDate)
const hoursToAccessExpiration = Math.floor((accessExpirationDate - correctedTime) /1000/ 60 /60)
if(hoursToAccessExpiration >= (7*24)) {
upgradeCardHeaderText = <FormattedMessage
id="learning.outline.alert.upgradecard.accessExpiration"
defaultMessage="Upgrade your course today"
/>
</div>
<div className="col-6 col-md-12 col-lg-9 col-xl-8 p-0 pl-lg-2 text-center mt-md-2 mt-lg-0">
<div className="row w-100 m-0 justify-content-center">
<UpgradeButton
offer={offer}
onClick={logClick}
verifiedMode={verifiedMode}
/>
{onLearnMore && (
<div className="col-12">
<Button
variant="link"
size="sm"
className="pb-0"
onClick={onLearnMore}
aria-labelledby="outline-sidebar-upgrade-header"
>
{intl.formatMessage(messages.learnMore)}
</Button>
</div>
)}
</div>
</div>
expirationBanner = <AccessExpirationDateBanner accessExpirationDate={accessExpirationDate} timezoneFormatArgs={timezoneFormatArgs} />
upsellMessage = <UpsellFBEFarAwayCardContent />
} else { // more urgent messaging if there's less than 7 days left
upgradeCardHeaderText = <FormattedMessage
id="learning.outline.alert.upgradecard.accessExpirationUrgent"
defaultMessage="Course Access Expiration"
/>
expirationBanner = <ExpirationCountdown hoursToExpiration={hoursToAccessExpiration}/>
upsellMessage = <UpsellFBESoonCardContent accessExpirationDate={accessExpirationDate} timezoneFormatArgs={timezoneFormatArgs} />
}
}
} else { //FBE is turned off
upgradeCardHeaderText = <FormattedMessage
id="learning.outline.alert.upgradecard.pursueAverifiedCertificate"
defaultMessage="Pursue a verified certificate"
/>
upsellMessage = <UpsellNoFBECardContent />
}
return (
<section className="mb-4 card">
<h2 className="h5 m-3" id="outline-sidebar-upgrade-header">
{upgradeCardHeaderText}
</h2>
{expirationBanner}
<div className="p-3">
{upsellMessage}
</div>
<UpgradeButton
offer={offer}
onClick={logClick}
verifiedMode={verifiedMode}
className="ml-3 mr-3 mb-3"
/>
{offerCode}
</section>
);
}

View File

@@ -2,3 +2,19 @@
border: 1px solid $dark-500;
border-top: 5px solid $dark-500;
}
.upsell-warning{
background-color: #FCF1F4;
}
.upsell-warning-light{
background-color: #FFFAED;;
}
.discount-info {
border-top: 1px solid rgba(0, 0, 0, 0.125);
}
.inline-link-underline {
text-decoration: underline;
}