Skip to content

Commit 00fb5b4

Browse files
authored
Merge pull request #513 from topcoder-platform/TCA-995_certif-progress-on-course-page
TCA-995 certif progress on course page -> dev
2 parents 5862ce6 + 1946e61 commit 00fb5b4

File tree

7 files changed

+179
-23
lines changed

7 files changed

+179
-23
lines changed

src-ts/tools/learn/certification-details/certification-curriculum/curriculum-cards/CurriculumCard.module.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
}
128128

129129
.bottomActions {
130-
@media (min-width: 1151px) {
130+
@media (min-width: 1150px) {
131131
display: none;
132132
}
133133
}

src-ts/tools/learn/certification-details/certification-curriculum/curriculum-cards/course-card/CourseCard.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import {
1313
useHoursEstimateToRange,
1414
UserCertificationProgressStatus,
1515
} from '../../../../learn-lib'
16-
import { getCertificatePath, getCoursePath } from '../../../../learn.routes'
16+
import {
17+
getCertificatePath,
18+
getCoursePath,
19+
getLessonPathFromCurrentLesson,
20+
} from '../../../../learn.routes'
1721
import CurriculumCard from '../CurriculumCard'
1822

1923
import styles from './CourseCard.module.scss'
@@ -58,9 +62,10 @@ const CourseCard: FC<CourseCardProps> = (props: CourseCardProps) => {
5862
buttonStyle='primary'
5963
size='xs'
6064
label='Resume'
61-
route={getCoursePath(
65+
route={getLessonPathFromCurrentLesson(
6266
props.provider,
6367
props.certification.certification,
68+
props.progress?.currentLesson,
6469
)}
6570
/>
6671
)

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
gap: $space-xxxxl;
77
position: relative;
88

9-
@media (min-width: 1151px) {
9+
@media (min-width: 1150px) {
1010
padding-right: calc(445px + $space-xxl);
1111
}
1212

@@ -30,7 +30,7 @@
3030
padding-right: calc(40vw + $space-xxl * 2);
3131
}
3232

33-
@media (min-width: 1151px) {
33+
@media (min-width: 1150px) {
3434
padding-right: calc(445px + $space-xxxl * 2);
3535
}
3636
}

src-ts/tools/learn/course-details/CourseDetailsPage.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ import {
1717
CoursesProviderData,
1818
CourseTitle,
1919
ResourceProviderData,
20-
TCACertification,
21-
TCACertificationsProviderData,
22-
useGetAllTCACertifications,
2320
useGetCertification,
2421
useGetCourses,
2522
useGetResourceProvider,
@@ -31,8 +28,8 @@ import {
3128
import { getCoursePath } from '../learn.routes'
3229

3330
import { CourseCurriculum } from './course-curriculum'
34-
import styles from './CourseDetailsPage.module.scss'
3531
import { TCACertificationBanner } from './tca-certification-banner'
32+
import styles from './CourseDetailsPage.module.scss'
3633

3734
const CourseDetailsPage: FC<{}> = () => {
3835

@@ -198,6 +195,7 @@ const CourseDetailsPage: FC<{}> = () => {
198195
/>
199196

200197
<TCACertificationBanner
198+
userId={profile?.userId}
201199
className={styles.tcaCertBanner}
202200
fccCertificateId={certificate.id}
203201
/>

src-ts/tools/learn/course-details/tca-certification-banner/TCACertificationBanner.module.scss

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
border-radius: $space-sm;
66

77
padding: $space-xxl;
8+
9+
@include ltemd {
10+
padding: $space-lg;
11+
}
812
}
913

1014
.header {
@@ -14,6 +18,7 @@
1418

1519
svg {
1620
@include icon-size(47);
21+
flex: 0 0 auto;
1722
}
1823

1924
&Content {
@@ -27,6 +32,12 @@
2732
}
2833
}
2934

35+
.certTitle {
36+
display: flex;
37+
align-items: flex-start;
38+
gap: $space-xs;
39+
}
40+
3041
.desc {
3142
margin-top: $space-lg;
3243
}
@@ -40,4 +51,46 @@
4051
font-weight: $font-weight-bold;
4152
color: $turq-160;
4253
text-transform: uppercase;
54+
}
55+
56+
.externalLink {
57+
display: flex;
58+
height: 24px;
59+
align-items: center;
60+
color: $turq-160;
61+
62+
svg {
63+
@include icon-lg;
64+
}
65+
}
66+
67+
.statusBox {
68+
display: flex;
69+
align-items: center;
70+
71+
background: $black-5;
72+
border-radius: $space-sm;
73+
74+
margin-top: $space-lg;
75+
padding: $space-sm;
76+
padding-left: 0;
77+
78+
.icon {
79+
display: flex;
80+
width: 40px;
81+
height: 24px;
82+
align-items: center;
83+
justify-content: center;
84+
flex: 0 0 auto;
85+
86+
color: $black-40;
87+
88+
svg {
89+
@include icon-xxl;
90+
}
91+
92+
&:global(.green) {
93+
color: $turq-75;
94+
}
95+
}
4396
}

src-ts/tools/learn/course-details/tca-certification-banner/TCACertificationBanner.tsx

Lines changed: 108 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,44 @@
11
/* eslint-disable react/no-danger */
2-
import { FC, useMemo } from 'react'
2+
import { FC, ReactNode, useMemo } from 'react'
33
import { Link } from 'react-router-dom'
44
import classNames from 'classnames'
55

6+
import { IconOutline, IconSolid } from '../../../../lib'
67
import {
78
CertificateBadgeIcon,
9+
LearnUserCertificationProgress,
810
TCACertification,
11+
TCACertificationProgressProviderData,
912
TCACertificationsProviderData,
1013
useGetAllTCACertifications,
14+
useGetTCACertificationProgress,
15+
useGetUserCertifications,
16+
UserCertificationProgressStatus,
17+
UserCertificationsProviderData,
1118
} from '../../learn-lib'
1219
import { getTCACertificationPath } from '../../learn.routes'
1320

1421
import styles from './TCACertificationBanner.module.scss'
1522

23+
interface ProgressByIdCollection {
24+
[key: string]: LearnUserCertificationProgress
25+
}
26+
27+
function getStatusBox(icon: ReactNode, text: string, theme: string = 'gray'): ReactNode {
28+
return (
29+
<div className={styles.statusBox}>
30+
<div className={classNames(styles.icon, theme)}>
31+
{icon}
32+
</div>
33+
<div className='body-small'>
34+
{text}
35+
</div>
36+
</div>
37+
)
38+
}
39+
1640
export interface TCACertificationBannerProps {
41+
userId?: number
1742
className?: string
1843
fccCertificateId?: string
1944
}
@@ -30,12 +55,71 @@ const TCACertificationBanner: FC<TCACertificationBannerProps> = (props: TCACerti
3055
))
3156
), [tcaCertifications, props.fccCertificateId])
3257

58+
// Fetch Enrollment status & progress
59+
const {
60+
progress: certifProgress,
61+
ready: certifProgressReady,
62+
}: TCACertificationProgressProviderData = useGetTCACertificationProgress(
63+
props.userId as unknown as string,
64+
certification?.dashedName as string,
65+
{ enabled: !!certification && !!props.userId },
66+
)
67+
68+
// Fetch the User's progress for all the tca certification's courses
69+
// so we can show their progress even before they enroll with the certification
70+
const {
71+
progresses: certsProgress,
72+
}: UserCertificationsProviderData = useGetUserCertifications('freeCodeCamp', {
73+
enabled: certifProgressReady && !certifProgress,
74+
})
75+
76+
const progressById: ProgressByIdCollection = useMemo(() => (
77+
certsProgress?.reduce((all, progress) => {
78+
all[progress.certificationId] = progress
79+
return all
80+
}, {} as ProgressByIdCollection) ?? {}
81+
), [certsProgress])
82+
3383
if (!certification) {
3484
return <></>
3585
}
3686

3787
const certifUrl: string = getTCACertificationPath(certification.dashedName)
3888

89+
function renderStatusBox(): ReactNode {
90+
if (!certification) {
91+
return <></>
92+
}
93+
94+
const coursesCount: number = certification.coursesCount
95+
const completedCoursesCount: number = certifProgress
96+
? Math.round(coursesCount * (certifProgress.certificationProgress / 100))
97+
: certification.certificationResources.filter(d => (
98+
progressById[d.freeCodeCampCertification.fccId]?.status === UserCertificationProgressStatus.completed
99+
)).length
100+
101+
if (!completedCoursesCount) {
102+
return certifProgress ? getStatusBox(
103+
<IconOutline.DotsCircleHorizontalIcon />,
104+
'Begin working towards earning this Topcoder Certification by starting this course today!',
105+
) : <></>
106+
}
107+
108+
if (completedCoursesCount === 1) {
109+
return getStatusBox(
110+
<IconOutline.ClockIcon />,
111+
`Good job! You are making progress with 1 of ${coursesCount} required courses.`,
112+
'green',
113+
)
114+
}
115+
116+
return getStatusBox(
117+
<IconSolid.CheckCircleIcon />,
118+
`You have completed ${completedCoursesCount} of ${coursesCount} required courses.`,
119+
'green',
120+
)
121+
}
122+
39123
return (
40124
<div className={classNames(props.className, styles.wrap)}>
41125
<div className={styles.header}>
@@ -47,23 +131,34 @@ const TCACertificationBanner: FC<TCACertificationBannerProps> = (props: TCACerti
47131
<div className='overline'>
48132
This course is part of a topcoder certification:
49133
</div>
50-
<div className='body-main-bold'>
134+
<div className={classNames(styles.certTitle, 'body-main-bold')}>
51135
{certification.title}
136+
{!!certifProgress && (
137+
<Link className={styles.externalLink} to={certifUrl}>
138+
<IconOutline.ExternalLinkIcon />
139+
</Link>
140+
)}
52141
</div>
53142
</div>
54143
</div>
55144

56-
<p className={classNames(styles.desc, 'body-small')}>
57-
{certification.description}
58-
</p>
59-
60-
<Link
61-
className={styles.link}
62-
title='Learn more'
63-
to={certifUrl}
64-
>
65-
Learn more
66-
</Link>
145+
{!certifProgress && (
146+
<p className={classNames(styles.desc, 'body-small')}>
147+
{certification.description}
148+
</p>
149+
)}
150+
151+
{renderStatusBox()}
152+
153+
{!certifProgress && (
154+
<Link
155+
className={styles.link}
156+
title='Learn more'
157+
to={certifUrl}
158+
>
159+
Learn more
160+
</Link>
161+
)}
67162
</div>
68163
)
69164
}

src-ts/tools/learn/learn-lib/data-providers/user-certifications-provider/user-certifications.provider.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@ import { UserCertificationInProgress } from './user-certification-in-progress.mo
99
import { LearnUserCertificationProgress, UserCertificationProgressStatus } from './user-certifications-functions'
1010
import { UserCertificationsProviderData } from './user-certifications-provider-data.model'
1111

12+
interface GetUserCertificationsOptions {
13+
enabled?: boolean
14+
}
15+
1216
export function useGetUserCertifications(
1317
provider: string = 'freeCodeCamp',
18+
options: GetUserCertificationsOptions = {} as GetUserCertificationsOptions,
1419
): UserCertificationsProviderData {
1520
const profileContextData: ProfileContextData = useContext<ProfileContextData>(profileContext)
1621
const userId: number | undefined = profileContextData?.profile?.userId
@@ -25,7 +30,7 @@ export function useGetUserCertifications(
2530
const url: string = learnUrlGet('certification-progresses', params)
2631

2732
const { data, error }: SWRResponse<ReadonlyArray<LearnUserCertificationProgress>> = useSWR(url, {
28-
isPaused: () => !userId,
33+
isPaused: () => !userId || options?.enabled === false,
2934
})
3035
const loading: boolean = !data && !error
3136

0 commit comments

Comments
 (0)