diff --git a/README.md b/README.md index 40693f457..68e7799bf 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,13 @@ The `yarn start` command serves the site using the cert and key in the /ssl dire By overriding the app to use port 443, you can use the authorized URL and trust the root CA to avoid SSL errors in the browser. ->**NOTE:** Mac users will require running the app with elevated permissions in order to use a port lower than 500. +>**NOTE:** Mac users will require running the app with elevated permissions in order to use a port lower than 500. + +Easy way to overcome elevated permissions is to make use of: + +``` +sudo setcap 'cap_net_bind_service=+ep' `which node` +``` For easier development, it is recommended that you add this certificate to your trusted root authorities and as a trused cert in your browser. Google your browser and OS for more info on how to trust cert authorities. diff --git a/src-ts/lib/svgs/icon-level-1.svg b/src-ts/lib/svgs/icon-level-1.svg new file mode 100644 index 000000000..93d0bce55 --- /dev/null +++ b/src-ts/lib/svgs/icon-level-1.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src-ts/lib/svgs/icon-level-2.svg b/src-ts/lib/svgs/icon-level-2.svg new file mode 100644 index 000000000..6dbb0207b --- /dev/null +++ b/src-ts/lib/svgs/icon-level-2.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src-ts/lib/svgs/icon-level-3.svg b/src-ts/lib/svgs/icon-level-3.svg new file mode 100644 index 000000000..1e276a1ad --- /dev/null +++ b/src-ts/lib/svgs/icon-level-3.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src-ts/lib/svgs/index.ts b/src-ts/lib/svgs/index.ts index 3d20722c1..5083bbf0d 100644 --- a/src-ts/lib/svgs/index.ts +++ b/src-ts/lib/svgs/index.ts @@ -19,6 +19,13 @@ import { ReactComponent as SocialShareTwitter } from './social-share-twitter.svg import { ReactComponent as SocialIconTwitter } from './social-tw-icon.svg' import { ReactComponent as SocialIconYoutube } from './social-yt-icon.svg' import { ReactComponent as TooltipArrowIcon } from './tooltip-arrow.svg' +import { ReactComponent as TcAcademyLogoSvg } from './tc-academy-logo.svg' +import { ReactComponent as TcLogoSvg } from './tc-logo.svg' +import { ReactComponent as FccLogoSvg } from './vendor-fcc-logo.svg' +import { ReactComponent as FccLogoBlackSvg } from './vendor-fcc-logo-black.svg' +import { ReactComponent as IconLevel1 } from './icon-level-1.svg' +import { ReactComponent as IconLevel2 } from './icon-level-2.svg' +import { ReactComponent as IconLevel3 } from './icon-level-3.svg' export { ActiveTabTipIcon, @@ -40,5 +47,12 @@ export { GithubIcon, SaveForLaterIcon, IconCheck, + TcAcademyLogoSvg, + TcLogoSvg, + FccLogoSvg, + FccLogoBlackSvg, + IconLevel1, + IconLevel2, + IconLevel3, } export * from './icon-wrapper' diff --git a/src-ts/tools/learn/course-certificate/certificate-view/certificate/tc-academy-logo.svg b/src-ts/lib/svgs/tc-academy-logo.svg similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/certificate/tc-academy-logo.svg rename to src-ts/lib/svgs/tc-academy-logo.svg diff --git a/src-ts/tools/learn/course-certificate/certificate-view/certificate/tc-logo.svg b/src-ts/lib/svgs/tc-logo.svg similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/certificate/tc-logo.svg rename to src-ts/lib/svgs/tc-logo.svg diff --git a/src-ts/lib/svgs/vendor-fcc-logo-black.svg b/src-ts/lib/svgs/vendor-fcc-logo-black.svg new file mode 100644 index 000000000..8cda25da7 --- /dev/null +++ b/src-ts/lib/svgs/vendor-fcc-logo-black.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src-ts/tools/learn/course-certificate/certificate-view/certificate/vendor-fcc-logo.svg b/src-ts/lib/svgs/vendor-fcc-logo.svg similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/certificate/vendor-fcc-logo.svg rename to src-ts/lib/svgs/vendor-fcc-logo.svg diff --git a/src-ts/tools/learn/course-certificate/certificate-view/certificate/Certificate.tsx b/src-ts/tools/learn/course-certificate/certificate-view/certificate/Certificate.tsx index 2071dff61..273bb93af 100644 --- a/src-ts/tools/learn/course-certificate/certificate-view/certificate/Certificate.tsx +++ b/src-ts/tools/learn/course-certificate/certificate-view/certificate/Certificate.tsx @@ -6,10 +6,9 @@ import { LearnCertificateTrackType } from '../../../learn-lib' import { CertificateBgPattern } from './certificate-bg-pattern' import { CourseCard } from './course-card' -import { ReactComponent as TcAcademyLogoSvg } from './tc-academy-logo.svg' -import { ReactComponent as TcLogoSvg } from './tc-logo.svg' -import { ReactComponent as FccLogoSvg } from './vendor-fcc-logo.svg' + import styles from './Certificate.module.scss' +import { FccLogoSvg, TcAcademyLogoSvg, TcLogoSvg } from '../../../../../lib' interface CertificateProps { completedDate?: string diff --git a/src-ts/tools/learn/learn-lib/data-providers/index.ts b/src-ts/tools/learn/learn-lib/data-providers/index.ts index ae66884e9..0d0093e10 100644 --- a/src-ts/tools/learn/learn-lib/data-providers/index.ts +++ b/src-ts/tools/learn/learn-lib/data-providers/index.ts @@ -4,3 +4,4 @@ export * from './lesson-provider' export * from './resource-provider-provider' export * from './user-certifications-provider' export * from './user-completed-certifications-provider' +export * from './tca-certifications-provider' diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/index.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/index.ts new file mode 100644 index 000000000..ca2a9041f --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/index.ts @@ -0,0 +1,5 @@ +export * from './tca-certifications-provider-data.model' +export * from './tca-certifications.provider' +export * from './tca-certificate-status-type' +export * from './tca-certificate-level-type' +export * from './tca-certification.model' diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-level-type.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-level-type.ts new file mode 100644 index 000000000..098237cfa --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-level-type.ts @@ -0,0 +1 @@ +export type TCACertificationLearnLevel = 'Beginner' | 'Intermediate' | 'Expert' | 'All Levels' diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-status-type.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-status-type.ts new file mode 100644 index 000000000..de5fb10cd --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certificate-status-type.ts @@ -0,0 +1 @@ +export type TCACertificationStatus = 'active' | 'inactive' | 'coming_soon' | 'deprecated' diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification.model.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification.model.ts new file mode 100644 index 000000000..8aab5a9ff --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification.model.ts @@ -0,0 +1,15 @@ +import { TCACertificationLearnLevel } from './tca-certificate-level-type' +import { TCACertificationStatus } from './tca-certificate-status-type' + +export interface TCACertification { + id: number + title: string + description: string + estimatedCompletionTime: number + status: TCACertificationStatus + sequentialCourses: boolean + learnerLevel: TCACertificationLearnLevel + certificationCategoryId: string + stripeProductId?: string + skills: string[] +} diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications-provider-data.model.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications-provider-data.model.ts new file mode 100644 index 000000000..451f60f0d --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications-provider-data.model.ts @@ -0,0 +1,8 @@ +import { TCACertification } from './tca-certification.model' + +export interface TCACertificationsProviderData { + certifications: Array + error: boolean + loading: boolean + ready: boolean +} diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications.provider.tsx b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications.provider.tsx new file mode 100644 index 000000000..8508d7165 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certifications.provider.tsx @@ -0,0 +1,71 @@ +/* eslint-disable max-len */ +/* eslint-disable sort-keys */ +/* eslint-disable default-param-last */ +import useSWR, { SWRConfiguration, SWRResponse } from 'swr' + +import { learnUrlGet } from '../../functions' +import { useSwrCache } from '../../learn-swr' + +import { TCACertificationsProviderData } from './tca-certifications-provider-data.model' +import { TCACertification } from './tca-certification.model' + +interface TCACertificationsAllProviderOptions { + enabled?: boolean +} + +export function useGetAllTCACertifications( + options?: TCACertificationsAllProviderOptions, +): TCACertificationsProviderData { + + const url: string = learnUrlGet( + 'topcoder-certifications', + ) + const swrCacheConfig: SWRConfiguration = useSwrCache(url) + + const { data, error }: SWRResponse = useSWR(url, { + ...swrCacheConfig, + isPaused: () => options?.enabled === false, + }) + + return { + certifications: data ?? [], + error: !!error, + loading: !data, + ready: !!data, + } +} + +// TODO: remove when integrated with API +export function useGetAllTCACertificationsMOCK(): TCACertificationsProviderData { + const data: TCACertification[] = [{ + id: 1, + title: 'Web Development Fundamentals', + description: 'The Web Developer Fundamentals certification will teach you the basics of HTML, CSS, javascript, front end libraries and will also introduce you to backend development.', + estimatedCompletionTime: 4, + learnerLevel: 'Beginner', + sequentialCourses: false, + status: 'active', + certificationCategoryId: '', + skills: ['HTML', 'CSS', 'JavaScript', 'HTML', 'CSS', 'JavaScript', 'HTML', 'CSS', 'JavaScript', 'HTML', 'CSS', 'JavaScript', 'HTML', 'CSS', 'JavaScript'], + }, + { + id: 2, + title: 'Data Science Fundamentals', + description: 'The Data Science Fundamentals certification will teach you the basics of scientific computing, Data Analysis and machine learning while using Python. Additionally, you will learn about data visualization.', + estimatedCompletionTime: 14, + status: 'active', + sequentialCourses: false, + learnerLevel: 'Expert', + certificationCategoryId: '', + skills: ['Python', 'TensorFlow', 'JSON'], + }] + + const error = {} + + return { + certifications: data ?? [], + error: !!error, + loading: !data, + ready: !!data, + } +} diff --git a/src-ts/tools/learn/learn-lib/svgs/cert-icon.svg b/src-ts/tools/learn/learn-lib/svgs/cert-icon.svg new file mode 100644 index 000000000..1be452e4e --- /dev/null +++ b/src-ts/tools/learn/learn-lib/svgs/cert-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src-ts/tools/learn/learn-lib/svgs/course-icon.svg b/src-ts/tools/learn/learn-lib/svgs/course-icon.svg new file mode 100644 index 000000000..37ff1622f --- /dev/null +++ b/src-ts/tools/learn/learn-lib/svgs/course-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src-ts/tools/learn/learn-lib/svgs/crowd-icon.svg b/src-ts/tools/learn/learn-lib/svgs/crowd-icon.svg new file mode 100644 index 000000000..999301dde --- /dev/null +++ b/src-ts/tools/learn/learn-lib/svgs/crowd-icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src-ts/tools/learn/learn-lib/svgs/index.ts b/src-ts/tools/learn/learn-lib/svgs/index.ts index 224066d9b..b20435b2a 100644 --- a/src-ts/tools/learn/learn-lib/svgs/index.ts +++ b/src-ts/tools/learn/learn-lib/svgs/index.ts @@ -1,3 +1,11 @@ import { ReactComponent as LearningHat } from './learning-hat.svg' +import { ReactComponent as CertIcon } from './cert-icon.svg' +import { ReactComponent as CourseIcon } from './course-icon.svg' +import { ReactComponent as CrowdIcon } from './crowd-icon.svg' -export { LearningHat } +export { + LearningHat, + CertIcon, + CourseIcon, + CrowdIcon, +} diff --git a/src-ts/tools/learn/welcome/WelcomePage.tsx b/src-ts/tools/learn/welcome/WelcomePage.tsx index cb154b27e..723fdc458 100644 --- a/src-ts/tools/learn/welcome/WelcomePage.tsx +++ b/src-ts/tools/learn/welcome/WelcomePage.tsx @@ -2,10 +2,12 @@ import { FC } from 'react' import classNames from 'classnames' import { PageSubheaderPortalId } from '../../../config' -import { ContentLayout, LoadingSpinner, Portal } from '../../../lib' +import { ContentLayout, LoadingSpinner, PageDivider, Portal } from '../../../lib' import { AllCertificationsProviderData, + TCACertificationsProviderData, useGetAllCertifications, + useGetAllTCACertificationsMOCK, useGetUserCertifications, UserCertificationsProviderData, WaveHero, @@ -14,6 +16,8 @@ import '../../../lib/styles/index.scss' import { AvailableCoursesList } from './available-courses-list' import { ProgressBlock } from './progress-block' +import { WhatTCACanDo } from './what-tca-cando' +import { TCCertifications } from './tc-certifications' import { ReactComponent as TcAcademyFullLogoSvg } from './tca-full-logo.svg' import styles from './WelcomePage.module.scss' @@ -24,6 +28,9 @@ const WelcomePage: FC = () => { const coursesReady: boolean = allCertsData.ready && userCertsData.ready + // TODO: this hook is mocked - remove mock when API is available... + const allTCACertifications: TCACertificationsProviderData = useGetAllTCACertificationsMOCK() + return ( @@ -42,9 +49,7 @@ const WelcomePage: FC = () => { The Topcoder Academy will provide you with learning opportunities in the form of guided learning paths. You will have the opportunity to learn new skills that will better - prepare you to earn on the Topcoder platform.
-
- We look forward to learning with you! + prepare you to earn on the Topcoder platform. `} theme='light' > @@ -61,6 +66,14 @@ const WelcomePage: FC = () => {
+ + + + + + + + {coursesReady && ( h2 { + color: $tc-white; + margin-bottom: $space-lg; + font-family: $font-barlow; - + .courses-group-title { - margin-top: $space-mxx; + @include ltemd { + font-size: 24px; + } + } - @include ltemd { - margin-top: $space-xxxxl; + >p { + color: $tc-white; + max-width: $md-max; } } -} -.courses-group-title { - margin-top: $space-xxl; - padding: $space-xxl 0; - border-top: $border solid $black-10; + .coursesListHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: $space-xxl; + border-bottom: 2px solid $black-10; + // margin-bottom: $space-xxl; - @include ltemd { - padding: $space-lg 0; - } -} + @include ltemd { + flex-direction: column; + align-items: flex-start; -.courses-list-header { - display: flex; - align-items: center; + h2 { + margin: $space-md 0 $space-xxl; + } + } - > h3 { - display: flex; - align-items: center; - gap: $space-sm; - } + h2 { + display: flex; + align-items: center; - @include ltemd { - flex-direction: column; - align-items: flex-start; - gap: $space-xxl; - } -} + .badge { + font-family: $font-roboto; + background: $blue-100; -.badge { - font-family: $font-roboto; - background: $blue-100; + padding: 0 $space-sm; + margin-left: $space-sm; + border-radius: 50px; + color: $tc-white; + } + } - padding: 0 $space-sm; - border-radius: 50px; - color: $tc-white; -} -.courses-list-filters { - display: flex; - margin-left: auto; + .coursesListFilters { + display: flex; + margin-left: auto; - gap: $space-xxl; + gap: $space-xxl; - > * { - min-width: 326px; - } + >* { + min-width: 326px; + } + + > :global(.input-wrapper) { + width: 100%; - > :global(.input-wrapper) { - width: 100%; + > :global(.input-el) { + margin: 0; + } + } - > :global(.input-el) { - margin: 0; + @include ltelg { + flex-direction: column; + align-items: flex-start; + gap: $space-lg; + } + + @include ltemd { + width: 100%; + } } } +} - @include ltelg { - flex-direction: column; - align-items: flex-start; - gap: $space-lg; +.coursesList { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: $space-xl; + padding: $space-xxl 0 $space-mxx; + border-bottom: 2px solid $black-10; + margin-bottom: $space-xxl; + + &:last-child { + border-bottom: none; } @include ltemd { - width: 100%; + grid-template-columns: 1fr; + padding: $space-lg 0 $space-mx; + margin-bottom: $space-lg; } -} +} \ No newline at end of file diff --git a/src-ts/tools/learn/welcome/available-courses-list/AvailableCoursesList.tsx b/src-ts/tools/learn/welcome/available-courses-list/AvailableCoursesList.tsx index 0880424e3..6e3227da0 100644 --- a/src-ts/tools/learn/welcome/available-courses-list/AvailableCoursesList.tsx +++ b/src-ts/tools/learn/welcome/available-courses-list/AvailableCoursesList.tsx @@ -1,5 +1,5 @@ import { Dictionary, groupBy, identity, orderBy } from 'lodash' -import { Dispatch, FC, Fragment, ReactNode, SetStateAction, useMemo } from 'react' +import { ChangeEvent, Dispatch, FC, Fragment, ReactNode, SetStateAction, useCallback, useMemo } from 'react' import classNames from 'classnames' import { InputSelect, useLocalStorage } from '../../../../lib' @@ -20,14 +20,14 @@ const PRIORITY_CATEGORIES: ReadonlyArray = [ ] const AvailableCoursesList: FC = (props: AvailableCoursesListProps) => { - const [selectedCategory, setSelectedCategory]: [ string, Dispatch> ] = useLocalStorage('tca-welcome-filter-certs', '') // certificates indexed by category, sorted by title - const certsByCategory: Dictionary> = useMemo(() => groupBy(orderBy(props.certifications, 'title', 'asc'), 'category'), [props.certifications]) + const certsByCategory: Dictionary> + = useMemo(() => groupBy(orderBy(props.certifications, 'title', 'asc'), 'category'), [props.certifications]) // compute all the available category dropdown options const certsCategoriesOptions: Array<{ @@ -53,17 +53,22 @@ const AvailableCoursesList: FC = (props: AvailableCou ['asc', 'asc'], ), [certsByCategory]) + const onSelectCategory: (e: ChangeEvent) => void + = useCallback((e: ChangeEvent) => { + setSelectedCategory(e.target.value as string) + }, [setSelectedCategory]) + const certificationsCount: number = ( (certsByCategory[selectedCategory] ?? props.certifications).length ) - const renderCertificationGroup = (category: string): ReactNode => ( + const renderCertificationGroup: (category: string) => ReactNode = (category: string) => (

{category}

-
+
{certsByCategory[category] .map(certification => ( = (props: AvailableCou return (
-
-

- Courses Available - - {certificationsCount} - -

- -
- setSelectedCategory(e.target.value as string)} - name='filter-courses' - label='Categories' - /> +
+
+

+ Courses + + {certificationsCount} + +

+
+ +
+
+ +
+

Check out our Courses

+

+ Topcoder is partnering with multiple content providers + to bring you a best in class course catalog. Stay tuned for more courses! +

diff --git a/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg-mobile.png b/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg-mobile.png new file mode 100644 index 000000000..531bb7187 Binary files /dev/null and b/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg-mobile.png differ diff --git a/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg.png b/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg.png new file mode 100644 index 000000000..3387f37bf Binary files /dev/null and b/src-ts/tools/learn/welcome/available-courses-list/assets/courses-banner-bg.png differ diff --git a/src-ts/tools/learn/welcome/courses-card/CoursesCard.module.scss b/src-ts/tools/learn/welcome/courses-card/CoursesCard.module.scss index 019b1ea12..7da9fea26 100644 --- a/src-ts/tools/learn/welcome/courses-card/CoursesCard.module.scss +++ b/src-ts/tools/learn/welcome/courses-card/CoursesCard.module.scss @@ -11,29 +11,87 @@ width: 100%; - :global(.overline) { - color: $black-60; + @include ltemd { + padding: $space-lg; + min-height: auto; } +} - &:global(.soon) { - :global(.badge-icon) { - opacity: 0.5; +.cardHeader { + display: flex; + + >div svg { + max-width: 48px; + max-height: 48px; + + @include ltemd { + max-width: 40px; + max-height: 40px; } } - @include ltemd { - padding: $space-lg; + .cardHeaderTitleWrap { + display: flex; + flex-direction: column; + margin-left: $space-lg; + + @include ltemd { + margin-left: $space-sm; + } + + .subTitleWrap { + display: flex; + align-items: center; + + >svg { + color: $blue-140; + margin-right: $space-xs; + } + + >em { + font-style: italic; + font-size: 14px; + line-height: 22px; + color: $black-60; + margin-right: $space-lg; + + &:last-child { + margin-right: 0; + } + } + } } } -.text { - color: $black-100; - :global(.quote-small) { - color: #767676; +.cardHeaderDividerWrap { + margin-top: auto; + + .cardHeaderDivider { + height: 1px; + border-bottom: $border solid $black-10; + width: 100%; } } -.bottom { - color: $blue-140; - margin-top: auto; +.cardBody { + display: flex; + flex-direction: column; + + .certProvider { + display: flex; + align-items: center; + color: $black-60; + font-style: italic; + font-size: 14px; + line-height: 22px; + + >svg { + margin-left: $space-sm; + fill: #0A0A23; + } + } } + +.cardBottom { + color: $blue-140; +} \ No newline at end of file diff --git a/src-ts/tools/learn/welcome/courses-card/CoursesCard.tsx b/src-ts/tools/learn/welcome/courses-card/CoursesCard.tsx index 0f1364e81..071c0071d 100644 --- a/src-ts/tools/learn/welcome/courses-card/CoursesCard.tsx +++ b/src-ts/tools/learn/welcome/courses-card/CoursesCard.tsx @@ -1,9 +1,9 @@ -import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react' +import { Dispatch, FC, memo, SetStateAction, useEffect, useState } from 'react' import classNames from 'classnames' -import { Button, ButtonStyle } from '../../../../lib' +import { Button, ButtonStyle, FccLogoBlackSvg, IconSolid, ProgressBar } from '../../../../lib' import { - CourseTitle, + CourseBadge, LearnCertification, UserCertificationCompleted, UserCertificationInProgress, @@ -19,15 +19,16 @@ interface CoursesCardProps { } const CoursesCard: FC = (props: CoursesCardProps) => { - - const [buttonStyle, setButtonStyle]: [ButtonStyle, Dispatch>] - = useState('primary') const [buttonLabel, setButtonLabel]: [string, Dispatch>] = useState('') const [link, setLink]: [string, Dispatch>] = useState('') - const courseEnabled: boolean = props.certification.state === 'active' + const [buttonStyle, setButtonStyle]: [string, Dispatch>] + = useState('secondary') + const [courseProgress, setCourseProgress]: [number | undefined, Dispatch>] + = useState(undefined) + useEffect(() => { // if the course isn't enabled, there's nothing to do @@ -44,7 +45,6 @@ const CoursesCard: FC = (props: CoursesCardProps) => { if (isCompleted) { // if the course is completed, View the Certificate - setButtonStyle('secondary') setButtonLabel('View Certificate') setLink(getCertificatePath( props.certification.providerName, @@ -53,8 +53,8 @@ const CoursesCard: FC = (props: CoursesCardProps) => { } else if (!inProgress) { // if there is no in-progress lesson for the course, - // Get Started by going to the course details - setButtonLabel('Get Started') + // Details by going to the course details + setButtonLabel('Details') setLink(getCoursePath( props.certification.providerName, props.certification.certification, @@ -63,13 +63,14 @@ const CoursesCard: FC = (props: CoursesCardProps) => { } else { // otherwise this course is in-progress, // so Resume the course at the next lesson - setButtonStyle('secondary') setButtonLabel('Resume') + setButtonStyle('primary') setLink(getLessonPathFromCurrentLesson( props.certification.providerName, props.certification.certification, inProgress.currentLesson, )) + setCourseProgress(inProgress.courseProgressPercentage / 100) } }, [ courseEnabled, @@ -80,19 +81,45 @@ const CoursesCard: FC = (props: CoursesCardProps) => { return (
-
- {props.certification.category} +
+ +
+

{props.certification.title}

+
+ + + {/* {props.certification.estimatedCompletionTime} */} + {' modules'} + + + + {props.certification.completionHours} + {' hours'} + +
+
- -
+ +
+ {courseProgress === undefined ? ( +
+ ) : ( + + )} +
+ +
+
+ {'by '} + +
+
+ +
{!!link && (