diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index b2baf28f22..bb7e7b5b60 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -32,6 +32,7 @@ "profile": "Profile", "news": "News", "donate": "Donate", + "support-our-nonprofit": "Support our non-profit", "update-settings": "Update my account settings", "sign-me-out": "Sign me out of freeCodeCamp", "flag-user": "Flag This User's Account for Abuse", @@ -290,6 +291,8 @@ }, "help-translate": "We are still translating the following certifications.", "help-translate-link": "Help us translate.", + "season-greetings": "Season's Greetings to you and your family.", + "if-getting-value": "If you're getting a lot out of freeCodeCamp, now is a great time to donate to support our nonprofit's mission.", "project-preview-title": "Here's a preview of what you will build" }, "donate": { diff --git a/client/src/components/Intro/index.tsx b/client/src/components/Intro/index.tsx index 75ff90cddc..4255af0455 100644 --- a/client/src/components/Intro/index.tsx +++ b/client/src/components/Intro/index.tsx @@ -1,5 +1,7 @@ +import { Alert } from '@freecodecamp/react-bootstrap'; import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; +import { emailToABVariant } from '../../utils/A-B-tester'; import { randomQuote } from '../../utils/get-words'; import Login from '../Header/components/Login'; import { Link, Spacer, Loader, FullWidthRow } from '../helpers'; @@ -16,6 +18,8 @@ interface IntroProps { pending?: boolean; slug?: string; username?: string; + email?: string; + onAlertClick?: () => void; } const Intro = ({ @@ -24,9 +28,16 @@ const Intro = ({ pending, complete, completedChallengeCount, - slug + slug, + email, + onAlertClick }: IntroProps): JSX.Element => { const { t } = useTranslation(); + const buttonVariation = (email: string | undefined): string => { + if (!email || emailToABVariant(email).isAVariant) + return t('buttons.donate'); + return t('buttons.support-our-nonprofit'); + }; if (pending && !complete) { return ( <> @@ -78,6 +89,24 @@ const Intro = ({ ) : ( '' )} + +

+ {t('learn.season-greetings')} +

+

{t('learn.if-getting-value')}

+

+


+ + {buttonVariation(email)} + +

+
); } else { diff --git a/client/src/components/helpers/link.tsx b/client/src/components/helpers/link.tsx index bc9dd2d672..f9704f5f39 100644 --- a/client/src/components/helpers/link.tsx +++ b/client/src/components/helpers/link.tsx @@ -8,6 +8,7 @@ interface LinkProps { sameTab?: boolean; state?: Record; to: string; + onClick?: () => void; } const Link = ({ diff --git a/client/src/components/layouts/global.css b/client/src/components/layouts/global.css index 6564d5cfc6..80ec16ee42 100644 --- a/client/src/components/layouts/global.css +++ b/client/src/components/layouts/global.css @@ -589,6 +589,32 @@ blockquote .small { overflow-wrap: break-word; } +.alert .btn { + background-color: #d9edf7; + color: #31708f; + border-color: #31708f; +} +.alert .btn:hover, +.alert .btn:focus { + background-color: #31708f; + color: #d9edf7; + border-color: #31708f; +} + +.annual-donation-alert { + background: linear-gradient( + -10deg, + rgba(217, 237, 247, 1) 35%, + rgba(237, 202, 216, 0) 75%, + rgb(255 215 224) 100% + ), + radial-gradient( + circle, + rgba(255, 202, 225, 1) 0%, + rgba(218, 234, 252, 1) 100% + ); +} + /* gatsby 404 */ #search { background-color: var(--quaternary-background); diff --git a/client/src/pages/learn.tsx b/client/src/pages/learn.tsx index 8c250a3ced..13022530a7 100644 --- a/client/src/pages/learn.tsx +++ b/client/src/pages/learn.tsx @@ -4,6 +4,7 @@ import React from 'react'; import Helmet from 'react-helmet'; import { useTranslation } from 'react-i18next'; import { connect } from 'react-redux'; +import { bindActionCreators, Dispatch } from 'redux'; import { createSelector } from 'reselect'; import Intro from '../components/Intro'; @@ -13,7 +14,8 @@ import LearnLayout from '../components/layouts/learn'; import { userFetchStateSelector, isSignedInSelector, - userSelector + userSelector, + executeGA } from '../redux'; interface FetchState { @@ -23,6 +25,7 @@ interface FetchState { } interface User { + email: string; name: string; username: string; completedChallengeCount: number; @@ -53,12 +56,18 @@ interface LearnPageProps { fields: Slug; }; }; + executeGA: (payload: Record) => void; } +const mapDispatchToProps = (dispatch: Dispatch) => + bindActionCreators({ executeGA }, dispatch); + function LearnPage({ isSignedIn, + user, fetchState: { pending, complete }, user: { name = '', completedChallengeCount = 0 }, + executeGA, data: { challengeNode: { fields: { slug } @@ -67,6 +76,16 @@ function LearnPage({ }: LearnPageProps) { const { t } = useTranslation(); + const onAlertClick = () => { + executeGA({ + type: 'event', + data: { + category: 'Donation Related', + action: `learn donation alert click` + } + }); + }; + return ( @@ -76,8 +95,10 @@ function LearnPage({ @@ -92,7 +113,7 @@ function LearnPage({ LearnPage.displayName = 'LearnPage'; -export default connect(mapStateToProps)(LearnPage); +export default connect(mapStateToProps, mapDispatchToProps)(LearnPage); export const query = graphql` query FirstChallenge { diff --git a/config/donation-settings.js b/config/donation-settings.js index ebe13a7f84..ab9bdd8367 100644 --- a/config/donation-settings.js +++ b/config/donation-settings.js @@ -94,7 +94,7 @@ const patreonDefaultPledgeAmount = 500; const aBTestConfig = { isTesting: true, - type: 'DistributionTest' + type: 'LearnAlertCTA' }; module.exports = {