diff --git a/src-ts/tools/learn/certification-details/enrollment-page/EnrollmentPage.tsx b/src-ts/tools/learn/certification-details/enrollment-page/EnrollmentPage.tsx index 37694e086..fe96a5423 100644 --- a/src-ts/tools/learn/certification-details/enrollment-page/EnrollmentPage.tsx +++ b/src-ts/tools/learn/certification-details/enrollment-page/EnrollmentPage.tsx @@ -78,7 +78,7 @@ const EnrollmentPage: FC<{}> = () => { if (progressReady && !enrolledCheck.current) { enrolledCheck.current = true if (!!progress) { - navigate(getTCACertificationPath(certification.dashedName)) + navigate(getTCACertificationPath(certification?.dashedName as string)) } } @@ -88,15 +88,17 @@ const EnrollmentPage: FC<{}> = () => { return } - await enrollTCACertificationAsync(`${profile.userId}`, `${certification.id}`) + await enrollTCACertificationAsync(`${profile.userId}`, `${certification?.id}`) .then(d => { setIsEnrolledModalOpen(true) setCertificateProgress(d) }) }, [certification?.id, profile, setCertificateProgress]) + const tcaMonetizationEnabled: boolean = EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION || false + function navToCertificationDetails(): void { - navigate(getTCACertificationPath(certification.dashedName)) + navigate(getTCACertificationPath(certification?.dashedName as string)) } function closeEnrolledModal(): void { @@ -110,8 +112,8 @@ const EnrollmentPage: FC<{}> = () => { @@ -131,7 +133,7 @@ const EnrollmentPage: FC<{}> = () => { useLayoutEffect(() => { if (profileReady && !profile) { - navigate(getTCACertificationPath(certification.dashedName)) + navigate(getTCACertificationPath(certification?.dashedName as string)) } }, [profileReady, profile, navigate, certification?.dashedName]) @@ -141,6 +143,7 @@ const EnrollmentPage: FC<{}> = () => { mainContent={renderMainContent()} extraBreadCrumbs={enrollmentBreadcrumb} certification={certification} + hideWaveHeroText={tcaMonetizationEnabled} /> ) } diff --git a/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.module.scss b/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.module.scss index 92cb54def..75612b550 100644 --- a/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.module.scss +++ b/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.module.scss @@ -3,10 +3,15 @@ @import '../../../../../lib/styles/inputs'; .payment-form { - .label { - @extend .body-ultra-small; - @extend .ultra-small-bold; - margin-bottom: $space-xs; + padding: $space-xxl; + + @include ltemd { + padding: $space-lg; + } + + > h3 { + margin-bottom: $space-xxl; + font-family: $font-barlow; } .cardElement { @@ -44,10 +49,31 @@ .pay-button { width: 100%; + + @include ltemd { + margin-top: $space-lg; + } } .error { - color: $red-100; + background-color: $red-25; + padding: $space-lg; + border: 1px solid $red-120; + border-radius: 4px; + color: $red-120; margin-bottom: $space-xl; + display: flex; + align-items: flex-start; + + .errorIcon { + width: 20px; + min-width: 20px; + margin-right: $space-sm; + } + + .errorMsg { + display: flex; + flex-direction: column; + } } } \ No newline at end of file diff --git a/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.tsx b/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.tsx index 49abe204d..3a29cdf44 100644 --- a/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.tsx +++ b/src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.tsx @@ -14,7 +14,7 @@ import { StripeCardNumberElementChangeEvent, } from '@stripe/stripe-js' -import { Button, InputText, LoadingSpinner, OrderContractModal } from '../../../../../lib' +import { Button, IconOutline, InputText, LoadingSpinner, OrderContractModal } from '../../../../../lib' import { InputWrapper } from '../../../../../lib/form/form-groups/form-input/input-wrapper' import styles from './EnrollPaymentForm.module.scss' @@ -32,12 +32,13 @@ interface FieldDirtyState { } interface EnrollPaymentFormProps { - error: boolean + error: string formData: PermiumSubFormData isFormValid: boolean onPay: () => void onUpdateField: (fieldName: string, value: string | boolean) => void isPayProcessing: boolean + price: string } type CardChangeEvent @@ -110,7 +111,19 @@ const EnrollPaymentForm: React.FC = (props: EnrollPaymen onClose={() => setIsOrderContractModalOpen(false)} /> -
Card Information
+

Enter your payment information

+ + { + props.error && ( +
+ +
+ Your payment has been declined + {props.error} +
+
+ ) + }
= (props: EnrollPaymen classes: { base: styles.cardElement, }, - placeholder: 'CCV', + placeholder: 'Enter CVC', }} onChange={(event: StripeCardCvcElementChangeEvent) => cardElementOnChange('cvvComplete', event, setCardCVVError)} /> @@ -196,14 +209,6 @@ const EnrollPaymentForm: React.FC = (props: EnrollPaymen onChange={event => props.onUpdateField('subsContract', event.target.checked)} /> - { - props.error && ( -
- Your card was declined. Please try a different card. -
- ) - } - { props.isPayProcessing && ( @@ -216,7 +221,7 @@ const EnrollPaymentForm: React.FC = (props: EnrollPaymen type='button' buttonStyle='primary' name='pay-button' - label='Complete Enrollment' + label={`Pay $${props.price} and enroll`} disable={!props.isFormValid || props.isPayProcessing} onClick={props.onPay} /> diff --git a/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.module.scss b/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.module.scss index b3031a035..7580d9311 100644 --- a/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.module.scss +++ b/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.module.scss @@ -1,9 +1,7 @@ @import '../../../../../lib/styles/includes'; -.wrap { - hr { - margin: $space-xxl 0; - } +.wrapPayment { + padding: 0; } .header { @@ -29,15 +27,37 @@ } } -.priceLabel { - color: $blue-140; - font-family: $font-barlow-condensed; - font-weight: 500; - font-size: 44px; - line-height: 44px; +.headerPayment { + background: $tc-grad20; text-align: center; - text-transform: uppercase; - margin-bottom: $space-xs; + padding: $space-xxxxl 0; + border-top-left-radius: $space-sm; + border-top-right-radius: $space-sm; + + .priceLabel { + color: $tc-white; + font-family: $font-barlow-condensed; + font-weight: 500; + font-size: 44px; + line-height: 44px; + text-align: center; + text-transform: uppercase; + } + + :global(.strike) { + text-decoration: line-through; + color: $black-20; + display: block; + font-size: 18px; + line-height: 22px; + font-family: $font-barlow; + font-weight: $font-weight-semibold; + margin-bottom: $space-sm; + } + + :global(.body-small-bold) { + color: $tc-white; + } } .noPaymentBanner { @@ -76,6 +96,27 @@ } } -.paymentSuccessIcon { - color: $green-100; + +.paymentSuccess { + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + padding: $space-xxl; + + .paymentSuccessInner { + background: $turq-15; + border: 1px solid $turq-160; + border-radius: 4px; + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: $space-xl $space-xl $space-mxx; + color: $turq-160; + + .successIcon { + min-width: 40px; + width: 40px; + margin-bottom: $space-md; + } + } } \ No newline at end of file diff --git a/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.tsx b/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.tsx index ada6439da..3cc351ec0 100644 --- a/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.tsx +++ b/src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.tsx @@ -5,7 +5,7 @@ import { PaymentIntentResult, Stripe, StripeCardNumberElement, StripeElements } import { loadStripe } from '@stripe/stripe-js/pure' import { CardNumberElement, Elements, useElements, useStripe } from '@stripe/react-stripe-js' -import { Button, IconSolid } from '../../../../../lib' +import { Button, IconOutline } from '../../../../../lib' import { StickySidebar } from '../../../learn-lib' import { EnvironmentConfig } from '../../../../../config' import { EnrollPaymentForm } from '../enroll-payment-form' @@ -42,11 +42,13 @@ const EnrollmentSidebar: FC = (props: EnrollmentSidebarP const elements: StripeElements | null = useElements() const [paymentError, setPaymentError]: [string, Dispatch>] = useState('') - const [paymentSuccess, setPaymentSuccess]: [any | undefined, Dispatch>] - = useState() + const [paymentSuccess, setPaymentSuccess]: [boolean, Dispatch>] + = useState(false) const [payProcessing, setPayProcessing]: [boolean, Dispatch>] = useState(false) + const tcaMonetizationEnabled: boolean = EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION || false + function onUpdateField(fieldName: string, value: string | boolean): void { setFormValues({ ...formFieldValues, @@ -97,11 +99,13 @@ const EnrollmentSidebar: FC = (props: EnrollmentSidebarP }, 1000) } else { // payment error! + // eslint-disable-next-line no-console console.error('Enroll payment error', paymentResult.error) setPaymentError(paymentResult.error.message as string) setPayProcessing(false) } } catch (error: any) { + // eslint-disable-next-line no-console console.error('Enroll payment error', error) setPaymentError(error.message || error) setPayProcessing(false) @@ -109,11 +113,11 @@ const EnrollmentSidebar: FC = (props: EnrollmentSidebarP } return ( - + { - EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION ? ( + tcaMonetizationEnabled ? ( <> -
+
$ {price} @@ -124,19 +128,28 @@ const EnrollmentSidebar: FC = (props: EnrollmentSidebarP TOTAL PAYMENT
-
{ paymentSuccess ? ( - +
+
+ + Your payment was successful +

+ You will be redirected to the certification details page + where you can begin your journey! +

+
+
) : ( ) } diff --git a/src-ts/tools/learn/certification-details/page-layout/PageLayout.tsx b/src-ts/tools/learn/certification-details/page-layout/PageLayout.tsx index 55cbcfc6f..f4faaaef5 100644 --- a/src-ts/tools/learn/certification-details/page-layout/PageLayout.tsx +++ b/src-ts/tools/learn/certification-details/page-layout/PageLayout.tsx @@ -26,6 +26,7 @@ interface PageLayoutProps { heroCTA?: ReactNode sidebarContents: ReactNode children?: ReactNode + hideWaveHeroText?: boolean } const PageLayout: FC = (props: PageLayoutProps) => { @@ -55,7 +56,7 @@ const PageLayout: FC = (props: PageLayoutProps) => { )} theme='grey' - text={props.certification.introText} + text={!props.hideWaveHeroText ? props.certification.introText : ''} > {props.heroCTA} diff --git a/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.module.scss b/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.module.scss index 25ad3992e..e23f7f520 100644 --- a/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.module.scss +++ b/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.module.scss @@ -93,3 +93,12 @@ } } } + +.aloneTeaseBanner { + margin-bottom: $space-mxx !important; + border-radius: 8px; + + @include ltemd { + margin-bottom: $space-xxl !important; + } +} diff --git a/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.tsx b/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.tsx index 4c192fce9..02c888fef 100644 --- a/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.tsx +++ b/src-ts/tools/learn/welcome/tc-certifications/TCCertifications.tsx @@ -3,6 +3,7 @@ import { FC, ReactNode, useCallback, useMemo } from 'react' import classNames from 'classnames' import { TCACertification, TCACertificationProgress } from '../../learn-lib' +import { EnvironmentConfig } from '../../../../config' import { TCCertCard } from './cert-card' import styles from './TCCertifications.module.scss' @@ -35,6 +36,8 @@ const TCCertifications: FC = (props: TCCertificationsProp const certificationsCount: number = props.certifications.length + const tcaCertMonetization: boolean = !!EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION + return (

@@ -43,18 +46,20 @@ const TCCertifications: FC = (props: TCCertificationsProp {certificationsCount}

-
+

Introducing Topcoder Certifications

We are happy to release Topcoder Certifications! Take advantage of our pilot Certification program. Click on a certification below to learn more.

-
- FREE -   - enrollment ends on April 30th. -
+ {!tcaCertMonetization && ( +
+ FREE +   + enrollment ends on April 30th. +
+ )}
{ diff --git a/src-ts/tools/learn/welcome/tc-certifications/cert-card/TCCertCard.tsx b/src-ts/tools/learn/welcome/tc-certifications/cert-card/TCCertCard.tsx index eac1aefe2..6546b0d98 100644 --- a/src-ts/tools/learn/welcome/tc-certifications/cert-card/TCCertCard.tsx +++ b/src-ts/tools/learn/welcome/tc-certifications/cert-card/TCCertCard.tsx @@ -1,6 +1,7 @@ import { FC, memo, ReactNode } from 'react' import classNames from 'classnames' +import { getTCACertificationPath, getTCAUserCertificationUrl } from '../../../learn.routes' import { Button, ButtonStyle, IconSolid, ProgressBar } from '../../../../../lib' import { CertificateBadgeIcon, @@ -12,7 +13,7 @@ import { TCACertificationProgress, TCACertificationProviderBase, } from '../../../learn-lib' -import { getTCACertificationPath, getTCAUserCertificationUrl } from '../../../learn.routes' +import { EnvironmentConfig } from '../../../../../config' import styles from './TCCertCard.module.scss' @@ -84,13 +85,6 @@ const TCCertCard: FC = (props: TCCertCardProps) => {
- {/* TODO: Uncomment this when paid certs come to prod! */} - {/*
- - - {' One time payment'} - -
*/}
) } @@ -123,7 +117,8 @@ const TCCertCard: FC = (props: TCCertCardProps) => {
NEW
-
FREE
+ {!EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION + &&
FREE
}
{renderStats()}