Skip to content

Commit c2fd187

Browse files
authored
Merge pull request #579 from topcoder-platform/TCA-920
TCA-719 enroll payment form tweaks -> dev
2 parents 92a0591 + dbfcf46 commit c2fd187

File tree

9 files changed

+161
-63
lines changed

9 files changed

+161
-63
lines changed

src-ts/tools/learn/certification-details/enrollment-page/EnrollmentPage.tsx

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const EnrollmentPage: FC<{}> = () => {
7878
if (progressReady && !enrolledCheck.current) {
7979
enrolledCheck.current = true
8080
if (!!progress) {
81-
navigate(getTCACertificationPath(certification.dashedName))
81+
navigate(getTCACertificationPath(certification?.dashedName as string))
8282
}
8383
}
8484

@@ -88,15 +88,17 @@ const EnrollmentPage: FC<{}> = () => {
8888
return
8989
}
9090

91-
await enrollTCACertificationAsync(`${profile.userId}`, `${certification.id}`)
91+
await enrollTCACertificationAsync(`${profile.userId}`, `${certification?.id}`)
9292
.then(d => {
9393
setIsEnrolledModalOpen(true)
9494
setCertificateProgress(d)
9595
})
9696
}, [certification?.id, profile, setCertificateProgress])
9797

98+
const tcaMonetizationEnabled: boolean = EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION || false
99+
98100
function navToCertificationDetails(): void {
99-
navigate(getTCACertificationPath(certification.dashedName))
101+
navigate(getTCACertificationPath(certification?.dashedName as string))
100102
}
101103

102104
function closeEnrolledModal(): void {
@@ -110,8 +112,8 @@ const EnrollmentPage: FC<{}> = () => {
110112
<PerksSection
111113
style='clear'
112114
items={perks}
113-
title={EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION
114-
? 'Enroll now with our introductory low pricing!'
115+
title={tcaMonetizationEnabled
116+
? ''
115117
: 'Enroll now for Free!'}
116118
/>
117119

@@ -131,7 +133,7 @@ const EnrollmentPage: FC<{}> = () => {
131133

132134
useLayoutEffect(() => {
133135
if (profileReady && !profile) {
134-
navigate(getTCACertificationPath(certification.dashedName))
136+
navigate(getTCACertificationPath(certification?.dashedName as string))
135137
}
136138
}, [profileReady, profile, navigate, certification?.dashedName])
137139

@@ -141,6 +143,7 @@ const EnrollmentPage: FC<{}> = () => {
141143
mainContent={renderMainContent()}
142144
extraBreadCrumbs={enrollmentBreadcrumb}
143145
certification={certification}
146+
hideWaveHeroText={tcaMonetizationEnabled}
144147
/>
145148
)
146149
}

src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.module.scss

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
@import '../../../../../lib/styles/inputs';
44

55
.payment-form {
6-
.label {
7-
@extend .body-ultra-small;
8-
@extend .ultra-small-bold;
9-
margin-bottom: $space-xs;
6+
padding: $space-xxl;
7+
8+
@include ltemd {
9+
padding: $space-lg;
10+
}
11+
12+
> h3 {
13+
margin-bottom: $space-xxl;
14+
font-family: $font-barlow;
1015
}
1116

1217
.cardElement {
@@ -44,10 +49,31 @@
4449

4550
.pay-button {
4651
width: 100%;
52+
53+
@include ltemd {
54+
margin-top: $space-lg;
55+
}
4756
}
4857

4958
.error {
50-
color: $red-100;
59+
background-color: $red-25;
60+
padding: $space-lg;
61+
border: 1px solid $red-120;
62+
border-radius: 4px;
63+
color: $red-120;
5164
margin-bottom: $space-xl;
65+
display: flex;
66+
align-items: flex-start;
67+
68+
.errorIcon {
69+
width: 20px;
70+
min-width: 20px;
71+
margin-right: $space-sm;
72+
}
73+
74+
.errorMsg {
75+
display: flex;
76+
flex-direction: column;
77+
}
5278
}
5379
}

src-ts/tools/learn/certification-details/enrollment-page/enroll-payment-form/EnrollPaymentForm.tsx

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
StripeCardNumberElementChangeEvent,
1515
} from '@stripe/stripe-js'
1616

17-
import { Button, InputText, LoadingSpinner, OrderContractModal } from '../../../../../lib'
17+
import { Button, IconOutline, InputText, LoadingSpinner, OrderContractModal } from '../../../../../lib'
1818
import { InputWrapper } from '../../../../../lib/form/form-groups/form-input/input-wrapper'
1919

2020
import styles from './EnrollPaymentForm.module.scss'
@@ -32,12 +32,13 @@ interface FieldDirtyState {
3232
}
3333

3434
interface EnrollPaymentFormProps {
35-
error: boolean
35+
error: string
3636
formData: PermiumSubFormData
3737
isFormValid: boolean
3838
onPay: () => void
3939
onUpdateField: (fieldName: string, value: string | boolean) => void
4040
isPayProcessing: boolean
41+
price: string
4142
}
4243

4344
type CardChangeEvent
@@ -110,7 +111,19 @@ const EnrollPaymentForm: React.FC<EnrollPaymentFormProps> = (props: EnrollPaymen
110111
onClose={() => setIsOrderContractModalOpen(false)}
111112
/>
112113

113-
<div className={styles.label}>Card Information</div>
114+
<h3>Enter your payment information</h3>
115+
116+
{
117+
props.error && (
118+
<div className={styles.error}>
119+
<IconOutline.ExclamationCircleIcon className={styles.errorIcon} />
120+
<div className={styles.errorMsg}>
121+
<strong>Your payment has been declined</strong>
122+
<span>{props.error}</span>
123+
</div>
124+
</div>
125+
)
126+
}
114127

115128
<div className={styles['input-wrap-wrapper']}>
116129
<InputWrapper
@@ -166,7 +179,7 @@ const EnrollPaymentForm: React.FC<EnrollPaymentFormProps> = (props: EnrollPaymen
166179
classes: {
167180
base: styles.cardElement,
168181
},
169-
placeholder: 'CCV',
182+
placeholder: 'Enter CVC',
170183
}}
171184
onChange={(event: StripeCardCvcElementChangeEvent) => cardElementOnChange('cvvComplete', event, setCardCVVError)}
172185
/>
@@ -196,14 +209,6 @@ const EnrollPaymentForm: React.FC<EnrollPaymentFormProps> = (props: EnrollPaymen
196209
onChange={event => props.onUpdateField('subsContract', event.target.checked)}
197210
/>
198211

199-
{
200-
props.error && (
201-
<div className={styles.error}>
202-
Your card was declined. Please try a different card.
203-
</div>
204-
)
205-
}
206-
207212
{
208213
props.isPayProcessing && (
209214
<LoadingSpinner type='Overlay' />
@@ -216,7 +221,7 @@ const EnrollPaymentForm: React.FC<EnrollPaymentFormProps> = (props: EnrollPaymen
216221
type='button'
217222
buttonStyle='primary'
218223
name='pay-button'
219-
label='Complete Enrollment'
224+
label={`Pay $${props.price} and enroll`}
220225
disable={!props.isFormValid || props.isPayProcessing}
221226
onClick={props.onPay}
222227
/>

src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.module.scss

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
@import '../../../../../lib/styles/includes';
22

3-
.wrap {
4-
hr {
5-
margin: $space-xxl 0;
6-
}
3+
.wrapPayment {
4+
padding: 0;
75
}
86

97
.header {
@@ -29,15 +27,37 @@
2927
}
3028
}
3129

32-
.priceLabel {
33-
color: $blue-140;
34-
font-family: $font-barlow-condensed;
35-
font-weight: 500;
36-
font-size: 44px;
37-
line-height: 44px;
30+
.headerPayment {
31+
background: $tc-grad20;
3832
text-align: center;
39-
text-transform: uppercase;
40-
margin-bottom: $space-xs;
33+
padding: $space-xxxxl 0;
34+
border-top-left-radius: $space-sm;
35+
border-top-right-radius: $space-sm;
36+
37+
.priceLabel {
38+
color: $tc-white;
39+
font-family: $font-barlow-condensed;
40+
font-weight: 500;
41+
font-size: 44px;
42+
line-height: 44px;
43+
text-align: center;
44+
text-transform: uppercase;
45+
}
46+
47+
:global(.strike) {
48+
text-decoration: line-through;
49+
color: $black-20;
50+
display: block;
51+
font-size: 18px;
52+
line-height: 22px;
53+
font-family: $font-barlow;
54+
font-weight: $font-weight-semibold;
55+
margin-bottom: $space-sm;
56+
}
57+
58+
:global(.body-small-bold) {
59+
color: $tc-white;
60+
}
4161
}
4262

4363
.noPaymentBanner {
@@ -76,6 +96,27 @@
7696
}
7797
}
7898

79-
.paymentSuccessIcon {
80-
color: $green-100;
99+
100+
.paymentSuccess {
101+
border-bottom-left-radius: 4px;
102+
border-bottom-right-radius: 4px;
103+
padding: $space-xxl;
104+
105+
.paymentSuccessInner {
106+
background: $turq-15;
107+
border: 1px solid $turq-160;
108+
border-radius: 4px;
109+
display: flex;
110+
flex-direction: column;
111+
align-items: center;
112+
text-align: center;
113+
padding: $space-xl $space-xl $space-mxx;
114+
color: $turq-160;
115+
116+
.successIcon {
117+
min-width: 40px;
118+
width: 40px;
119+
margin-bottom: $space-md;
120+
}
121+
}
81122
}

src-ts/tools/learn/certification-details/enrollment-page/enrollment-sidebar/EnrollmentSidebar.tsx

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { PaymentIntentResult, Stripe, StripeCardNumberElement, StripeElements }
55
import { loadStripe } from '@stripe/stripe-js/pure'
66
import { CardNumberElement, Elements, useElements, useStripe } from '@stripe/react-stripe-js'
77

8-
import { Button, IconSolid } from '../../../../../lib'
8+
import { Button, IconOutline } from '../../../../../lib'
99
import { StickySidebar } from '../../../learn-lib'
1010
import { EnvironmentConfig } from '../../../../../config'
1111
import { EnrollPaymentForm } from '../enroll-payment-form'
@@ -42,11 +42,13 @@ const EnrollmentSidebar: FC<EnrollmentSidebarProps> = (props: EnrollmentSidebarP
4242
const elements: StripeElements | null = useElements()
4343

4444
const [paymentError, setPaymentError]: [string, Dispatch<SetStateAction<string>>] = useState<string>('')
45-
const [paymentSuccess, setPaymentSuccess]: [any | undefined, Dispatch<SetStateAction<any | undefined>>]
46-
= useState<any | undefined>()
45+
const [paymentSuccess, setPaymentSuccess]: [boolean, Dispatch<SetStateAction<boolean>>]
46+
= useState<boolean>(false)
4747

4848
const [payProcessing, setPayProcessing]: [boolean, Dispatch<SetStateAction<boolean>>] = useState<boolean>(false)
4949

50+
const tcaMonetizationEnabled: boolean = EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION || false
51+
5052
function onUpdateField(fieldName: string, value: string | boolean): void {
5153
setFormValues({
5254
...formFieldValues,
@@ -97,23 +99,25 @@ const EnrollmentSidebar: FC<EnrollmentSidebarProps> = (props: EnrollmentSidebarP
9799
}, 1000)
98100
} else {
99101
// payment error!
102+
// eslint-disable-next-line no-console
100103
console.error('Enroll payment error', paymentResult.error)
101104
setPaymentError(paymentResult.error.message as string)
102105
setPayProcessing(false)
103106
}
104107
} catch (error: any) {
108+
// eslint-disable-next-line no-console
105109
console.error('Enroll payment error', error)
106110
setPaymentError(error.message || error)
107111
setPayProcessing(false)
108112
}
109113
}
110114

111115
return (
112-
<StickySidebar className={styles.wrap}>
116+
<StickySidebar className={tcaMonetizationEnabled ? styles.wrapPayment : styles.wrap}>
113117
{
114-
EnvironmentConfig.REACT_APP_ENABLE_TCA_CERT_MONETIZATION ? (
118+
tcaMonetizationEnabled ? (
115119
<>
116-
<div className={styles.header}>
120+
<div className={styles.headerPayment}>
117121
<div className={styles.priceLabel}>
118122
$
119123
{price}
@@ -124,19 +128,28 @@ const EnrollmentSidebar: FC<EnrollmentSidebarProps> = (props: EnrollmentSidebarP
124128
</span>
125129
<span className='body-small-bold'>TOTAL PAYMENT</span>
126130
</div>
127-
<hr />
128131
<div className={styles.form}>
129132
{
130133
paymentSuccess ? (
131-
<IconSolid.CheckCircleIcon className={styles.paymentSuccessIcon} />
134+
<div className={styles.paymentSuccess}>
135+
<div className={styles.paymentSuccessInner}>
136+
<IconOutline.CheckCircleIcon className={styles.successIcon} />
137+
<span className='body-medium-bold'>Your payment was successful</span>
138+
<p>
139+
You will be redirected to the certification details page
140+
where you can begin your journey!
141+
</p>
142+
</div>
143+
</div>
132144
) : (
133145
<EnrollPaymentForm
134146
formData={formFieldValues}
135147
onUpdateField={onUpdateField}
136148
onPay={onPay}
137149
isFormValid={isFormValid()}
138-
error={!!paymentError}
150+
error={paymentError}
139151
isPayProcessing={payProcessing}
152+
price={price}
140153
/>
141154
)
142155
}

src-ts/tools/learn/certification-details/page-layout/PageLayout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface PageLayoutProps {
2626
heroCTA?: ReactNode
2727
sidebarContents: ReactNode
2828
children?: ReactNode
29+
hideWaveHeroText?: boolean
2930
}
3031

3132
const PageLayout: FC<PageLayoutProps> = (props: PageLayoutProps) => {
@@ -55,7 +56,7 @@ const PageLayout: FC<PageLayoutProps> = (props: PageLayoutProps) => {
5556
<HeroTitle certification={props.certification} certTitle={props.certification.title} />
5657
)}
5758
theme='grey'
58-
text={props.certification.introText}
59+
text={!props.hideWaveHeroText ? props.certification.introText : ''}
5960
>
6061
{props.heroCTA}
6162
</WaveHero>

src-ts/tools/learn/welcome/tc-certifications/TCCertifications.module.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,12 @@
9393
}
9494
}
9595
}
96+
97+
.aloneTeaseBanner {
98+
margin-bottom: $space-mxx !important;
99+
border-radius: 8px;
100+
101+
@include ltemd {
102+
margin-bottom: $space-xxl !important;
103+
}
104+
}

0 commit comments

Comments
 (0)