diff --git a/src-ts/tools/learn/course-certificate/certificate-view/CertificateView.tsx b/src-ts/tools/learn/course-certificate/certificate-view/CertificateView.tsx index 9e6e1d428..fbb191f6b 100644 --- a/src-ts/tools/learn/course-certificate/certificate-view/CertificateView.tsx +++ b/src-ts/tools/learn/course-certificate/certificate-view/CertificateView.tsx @@ -1,7 +1,6 @@ import { FC, MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react' import { NavigateFunction, useNavigate } from 'react-router-dom' import classNames from 'classnames' -import html2canvas from 'html2canvas' import { FacebookSocialShareBtn, @@ -13,8 +12,12 @@ import { UserProfile, } from '../../../../lib' import { + ActionButton, AllCertificationsProviderData, CoursesProviderData, + useCertificateCanvas, + useCertificatePrint, + useCertificateScaling, useGetCertification, useGetCourses, useGetUserCompletedCertifications, @@ -22,9 +25,7 @@ import { } from '../../learn-lib' import { getCoursePath, getUserCertificateSsr } from '../../learn.routes' -import { ActionButton } from './action-button' import { Certificate } from './certificate' -import { useCertificateScaling } from './use-certificate-scaling.hook' import styles from './CertificateView.module.scss' export type CertificateViewStyle = 'large-container' | undefined @@ -102,27 +103,7 @@ const CertificateView: FC = (props: CertificateViewProps) navigate(coursePath) }, [coursePath, navigate]) - const getCertificateCanvas: () => Promise = useCallback(async () => { - - if (!certificateElRef.current) { - return undefined - } - - return html2canvas(certificateElRef.current, { - // when canvas iframe is ready, remove text gradients - // as they're not supported in html2canvas - onclone: (doc: Document) => { - [].forEach.call(doc.querySelectorAll('.grad'), (el: HTMLDivElement) => { - el.classList.remove('grad') - }) - }, - // scale (pixelRatio) doesn't matter for the final ceriticate, use 1 - scale: 1, - // use the same (ideal) window size when rendering the certificate - windowHeight: 700, - windowWidth: 1024, - }) - }, []) + const getCertificateCanvas: () => Promise = useCertificateCanvas(certificateElRef) const handleDownload: () => Promise = useCallback(async () => { @@ -133,23 +114,7 @@ const CertificateView: FC = (props: CertificateViewProps) }, [certificationTitle, getCertificateCanvas]) - const handlePrint: () => Promise = useCallback(async () => { - - const canvas: HTMLCanvasElement | void = await getCertificateCanvas() - if (!canvas) { - return - } - - const printWindow: Window | null = window.open('') - if (!printWindow) { - return - } - - printWindow.document.body.appendChild(canvas) - printWindow.document.title = certificationTitle - printWindow.focus() - printWindow.print() - }, [certificationTitle, getCertificateCanvas]) + const handlePrint: () => Promise = useCertificatePrint(certificateElRef, certificationTitle) useEffect(() => { if (ready && !hasCompletedTheCertification) { diff --git a/src-ts/tools/learn/course-certificate/certificate-view/action-button/ActionButton.module.scss b/src-ts/tools/learn/learn-lib/action-button/ActionButton.module.scss similarity index 86% rename from src-ts/tools/learn/course-certificate/certificate-view/action-button/ActionButton.module.scss rename to src-ts/tools/learn/learn-lib/action-button/ActionButton.module.scss index a80e1bf89..311d9cffb 100644 --- a/src-ts/tools/learn/course-certificate/certificate-view/action-button/ActionButton.module.scss +++ b/src-ts/tools/learn/learn-lib/action-button/ActionButton.module.scss @@ -1,4 +1,4 @@ -@import '../../../../../lib/styles/includes'; +@import '../../../../lib/styles/includes'; .wrap { @include icon-mxx; diff --git a/src-ts/tools/learn/course-certificate/certificate-view/action-button/ActionButton.tsx b/src-ts/tools/learn/learn-lib/action-button/ActionButton.tsx similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/action-button/ActionButton.tsx rename to src-ts/tools/learn/learn-lib/action-button/ActionButton.tsx diff --git a/src-ts/tools/learn/course-certificate/certificate-view/action-button/index.ts b/src-ts/tools/learn/learn-lib/action-button/index.ts similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/action-button/index.ts rename to src-ts/tools/learn/learn-lib/action-button/index.ts 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 0d0093e10..1e892d208 100644 --- a/src-ts/tools/learn/learn-lib/data-providers/index.ts +++ b/src-ts/tools/learn/learn-lib/data-providers/index.ts @@ -4,4 +4,5 @@ export * from './lesson-provider' export * from './resource-provider-provider' export * from './user-certifications-provider' export * from './user-completed-certifications-provider' +export * from './user-completed-tca-certifications-provider' export * from './tca-certifications-provider' diff --git a/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification-category.model.ts b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification-category.model.ts new file mode 100644 index 000000000..e0e3add02 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/tca-certifications-provider/tca-certification-category.model.ts @@ -0,0 +1,7 @@ +export interface TCACertificationCategory { + id: number + category: string + track: string + createdAt: Date + updatedAt: Date +} 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 index f53264d17..fe1c846ed 100644 --- 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 @@ -1,22 +1,26 @@ import { TCACertificationLearnLevel } from './tca-certificate-level-type' import { TCACertificationStatus } from './tca-certificate-status-type' +import { TCACertificationCategory } from './tca-certification-category.model' import { TcaProviderType } from './tca-provider-type' export interface TCACertification { + id: number + title: string certificationCategoryId: string coursesCount: number dashedName: string description: string - estimatedCompletionTime: number - id: number introText: string + estimatedCompletionTime: number learnerLevel: TCACertificationLearnLevel - learningOutcomes: Array - prerequisites: Array + certificationCategory: TCACertificationCategory + stripeProductId?: string + skills: string[] + learningOutcomes: string[] + prerequisites: string[] + createdAt: Date + updatedAt: Date providers: Array sequentialCourses: boolean - skills: string[] status: TCACertificationStatus - stripeProductId?: string - title: 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 index 451f60f0d..57d4a914f 100644 --- 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 @@ -6,3 +6,10 @@ export interface TCACertificationsProviderData { loading: boolean ready: boolean } + +export interface TCACertificationProviderData { + certification: TCACertification + 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 index adcbd5093..64b36848f 100644 --- 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 @@ -1,24 +1,51 @@ /* eslint-disable max-len */ /* eslint-disable sort-keys */ /* eslint-disable default-param-last */ +import { find } from 'lodash' import useSWR, { SWRConfiguration, SWRResponse } from 'swr' +import { LEARN_PATHS } from '../../../learn.routes' import { learnUrlGet } from '../../functions' import { useSwrCache } from '../../learn-swr' -import { TCACertificationsProviderData } from './tca-certifications-provider-data.model' +import { TCACertificationProviderData, TCACertificationsProviderData } from './tca-certifications-provider-data.model' import { TCACertification } from './tca-certification.model' interface TCACertificationsAllProviderOptions { enabled?: boolean } +const TCACertificationMock: TCACertification[] = [{ + id: 1, + title: 'Web Development Fundamentals', + dashedName: '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', + dashedName: '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'], +}] + export function useGetAllTCACertifications( options?: TCACertificationsAllProviderOptions, ): TCACertificationsProviderData { const url: string = learnUrlGet( - 'topcoder-certifications', + LEARN_PATHS.tcaCertifications, ) const swrCacheConfig: SWRConfiguration = useSwrCache(url) @@ -38,10 +65,10 @@ export function useGetAllTCACertifications( export function useGetTCACertification( certification: string, options?: TCACertificationsAllProviderOptions, -): TCACertificationsProviderData { +): TCACertificationProviderData { const url: string = learnUrlGet( - 'topcoder-certifications', + LEARN_PATHS.tcaCertifications, certification, ) const swrCacheConfig: SWRConfiguration = useSwrCache(url) @@ -52,7 +79,7 @@ export function useGetTCACertification( }) return { - certifications: data ?? [], + certification: data, error: !!error, loading: !data, ready: !!data, @@ -60,44 +87,14 @@ export function useGetTCACertification( } // TODO: remove when integrated with API -export function useGetAllTCACertificationsMOCK(): TCACertificationsProviderData { - const data: TCACertification[] = [{ - id: 1, - title: 'Web Development Fundamentals', - dashedName: 'web-developmnt-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.', - introText: 'Introducing our Web Development fundamentals certification! Start your certification journey with Topcoder.', - estimatedCompletionTime: 4, - learnerLevel: 'Beginner', - sequentialCourses: false, - status: 'active', - certificationCategoryId: '', - skills: ['HTML', 'CSS', 'JavaScript', 'HTML1', 'CSS2', 'JavaScript2', 'HTML3', 'CSS3', 'JavaScript3', 'HTML4', 'CSS4', 'JavaScript4'], - prerequisites: [], - coursesCount: 4, - providers: ['freecodecamp', 'topcoder'], - learningOutcomes: [], - }, - { - id: 2, - title: 'Data Science Fundamentals', - dashedName: '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.', - introText: '', - estimatedCompletionTime: 14, - status: 'active', - sequentialCourses: false, - learnerLevel: 'Expert', - certificationCategoryId: '', - skills: ['Python', 'TensorFlow', 'JSON'], - prerequisites: [], - coursesCount: 4, - providers: ['freecodecamp', 'topcoder'], - learningOutcomes: [], - }] +export function useGetTCACertificationMOCK( + certification: string, +): TCACertificationProviderData { + + const data: TCACertification | undefined = find(TCACertificationMock, { dashedName: certification }) return { - certifications: data ?? [], + certification: data, error: false, loading: !data, ready: !!data, @@ -105,36 +102,11 @@ export function useGetAllTCACertificationsMOCK(): TCACertificationsProviderData } // TODO: remove when integrated with API -export function useGetTCACertificationMOCK( - certification: string, -): TCACertificationsProviderData { - const data: TCACertification[] = [{ - id: 1, - title: 'Web Development Fundamentals', - dashedName: 'web-developmnt-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.', - introText: 'Introducing our Web Development fundamentals certification! Start your certification journey with Topcoder.', - estimatedCompletionTime: 4, - learnerLevel: 'Beginner', - sequentialCourses: false, - status: 'active', - certificationCategoryId: '', - skills: ['HTML', 'CSS', 'JavaScript', 'HTML1', 'CSS2', 'JavaScript2', 'HTML3', 'CSS3', 'JavaScript3', 'HTML4', 'CSS4', 'JavaScript4'], - prerequisites: [], - coursesCount: 4, - providers: ['freecodecamp', 'topcoder'], - learningOutcomes: [ - 'Fundamental skills required to begin a career in web development', - 'Introduction to React and other front end libraries - a jumping off point to build awesome websites', - 'Introduction to Java Script - one of the languages every web developer should know for web development and building basic algorithms and data structures', - 'Introduction to backend development with Node and APIs', - ], - }] - +export function useGetAllTCACertificationsMOCK(): TCACertificationsProviderData { return { - certifications: data ?? [], + certifications: TCACertificationMock ?? [], error: false, - loading: !data, - ready: !!data, + loading: !TCACertificationMock, + ready: !!TCACertificationMock, } } diff --git a/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/index.ts b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/index.ts new file mode 100644 index 000000000..40bf84940 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/index.ts @@ -0,0 +1,3 @@ +export * from './user-completed-tca-certifications-provider-data.model' +export * from './user-completed-tca-certifications.provider' +export * from './user-completed-tca-certification.model' diff --git a/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certification.model.ts b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certification.model.ts new file mode 100644 index 000000000..2e420ed42 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certification.model.ts @@ -0,0 +1,5 @@ +export interface UserCompletedTCACertification { + status: string + completedDate: string + trackType: string +} diff --git a/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications-provider-data.model.ts b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications-provider-data.model.ts new file mode 100644 index 000000000..925715b33 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications-provider-data.model.ts @@ -0,0 +1,7 @@ +import { UserCompletedTCACertification } from './user-completed-tca-certification.model' + +export interface UserCompletedTCACertificationsProviderData { + certifications: ReadonlyArray + loading: boolean + ready: boolean +} diff --git a/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications.provider.tsx b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications.provider.tsx new file mode 100644 index 000000000..a7f5b6f6c --- /dev/null +++ b/src-ts/tools/learn/learn-lib/data-providers/user-completed-tca-certifications-provider/user-completed-tca-certifications.provider.tsx @@ -0,0 +1,48 @@ +import useSWR, { SWRResponse } from 'swr' + +import { learnUrlGet } from '../../functions' + +import { UserCompletedTCACertification } from './user-completed-tca-certification.model' +import { UserCompletedTCACertificationsProviderData } from './user-completed-tca-certifications-provider-data.model' + +const COMPLETED_CERTS_MOCK = [ + { status: 'comleted', trackType: 'web dev', completedDate: 'Dec 19, 2022' }, +] + +export function useGetUserTCACompletedCertifications( + userId?: number, + certification?: string, +): UserCompletedTCACertificationsProviderData { + + // TODO: update to actual API endpoint URL when ready + const url: string = learnUrlGet('completed-certifications', `${userId}`) + + const { data, error }: SWRResponse> = useSWR(url) + + let certifications: ReadonlyArray = data ?? [] + + if (certification) { + certifications = certifications + .filter(c => (!certification || c.certification === certification)) + } + + return { + certifications, + loading: !data && !error, + ready: !!data || !!error, + } +} + +// TODO: remove when API ready +export function useGetUserTCACompletedCertificationsMOCK( + userId?: number, + certification?: string, +): UserCompletedTCACertificationsProviderData { + const data = COMPLETED_CERTS_MOCK + + return { + certifications: data, + loading: !data, + ready: !!data, + } +} diff --git a/src-ts/tools/learn/learn-lib/index.ts b/src-ts/tools/learn/learn-lib/index.ts index 4524fb12b..b72d49eb0 100755 --- a/src-ts/tools/learn/learn-lib/index.ts +++ b/src-ts/tools/learn/learn-lib/index.ts @@ -1,3 +1,4 @@ +export * from './action-button' export * from './collapsible-pane' export * from './course-badge' export * from './course-outline' @@ -9,4 +10,7 @@ export * from './learn-level-icon' export * from './learn-swr' export * from './my-course-card' export * from './svgs' +export * from './use-certificate-canvas-hook' +export * from './use-certificate-print-hook' +export * from './use-certificate-scaling-hook' export * from './wave-hero' diff --git a/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/index.ts b/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/index.ts new file mode 100644 index 000000000..76a886856 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/index.ts @@ -0,0 +1 @@ +export * from './useCertificateCanvas.hook' diff --git a/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/useCertificateCanvas.hook.tsx b/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/useCertificateCanvas.hook.tsx new file mode 100644 index 000000000..38536237e --- /dev/null +++ b/src-ts/tools/learn/learn-lib/use-certificate-canvas-hook/useCertificateCanvas.hook.tsx @@ -0,0 +1,30 @@ +import { MutableRefObject, useCallback } from 'react' +import html2canvas from 'html2canvas' + +export function useCertificateCanvas( + certificateElRef: MutableRefObject, +): () => Promise { + const getCertificateCanvas: () => Promise = useCallback(async () => { + + if (!certificateElRef.current) { + return undefined + } + + return html2canvas(certificateElRef.current, { + // when canvas iframe is ready, remove text gradients + // as they're not supported in html2canvas + onclone: (doc: Document) => { + [].forEach.call(doc.querySelectorAll('.grad'), (el: HTMLDivElement) => { + el.classList.remove('grad') + }) + }, + // scale (pixelRatio) doesn't matter for the final ceriticate, use 1 + scale: 1, + // use the same (ideal) window size when rendering the certificate + windowHeight: 700, + windowWidth: 1024, + }) + }, [certificateElRef]) + + return getCertificateCanvas +} diff --git a/src-ts/tools/learn/learn-lib/use-certificate-print-hook/index.ts b/src-ts/tools/learn/learn-lib/use-certificate-print-hook/index.ts new file mode 100644 index 000000000..62bbff159 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/use-certificate-print-hook/index.ts @@ -0,0 +1 @@ +export * from './useCertificatePrint.hook' diff --git a/src-ts/tools/learn/learn-lib/use-certificate-print-hook/useCertificatePrint.hook.tsx b/src-ts/tools/learn/learn-lib/use-certificate-print-hook/useCertificatePrint.hook.tsx new file mode 100644 index 000000000..22a2f7d73 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/use-certificate-print-hook/useCertificatePrint.hook.tsx @@ -0,0 +1,29 @@ +import { MutableRefObject, useCallback } from 'react' +import { useCertificateCanvas } from '../use-certificate-canvas-hook' + +export function useCertificatePrint( + certificateElRef: MutableRefObject, + certificationTitle: string, +): () => Promise { + const getCertificateCanvas: () => Promise = useCertificateCanvas(certificateElRef) + + const handlePrint: () => Promise = useCallback(async () => { + + const canvas: HTMLCanvasElement | void = await getCertificateCanvas() + if (!canvas) { + return + } + + const printWindow: Window | null = window.open('') + if (!printWindow) { + return + } + + printWindow.document.body.appendChild(canvas) + printWindow.document.title = certificationTitle + printWindow.focus() + printWindow.print() + }, [certificationTitle, getCertificateCanvas]) + + return handlePrint +} diff --git a/src-ts/tools/learn/learn-lib/use-certificate-scaling-hook/index.ts b/src-ts/tools/learn/learn-lib/use-certificate-scaling-hook/index.ts new file mode 100644 index 000000000..0a724eaa0 --- /dev/null +++ b/src-ts/tools/learn/learn-lib/use-certificate-scaling-hook/index.ts @@ -0,0 +1 @@ +export * from './useCertificateScaling.hook' diff --git a/src-ts/tools/learn/course-certificate/certificate-view/use-certificate-scaling.hook.tsx b/src-ts/tools/learn/learn-lib/use-certificate-scaling-hook/useCertificateScaling.hook.tsx similarity index 100% rename from src-ts/tools/learn/course-certificate/certificate-view/use-certificate-scaling.hook.tsx rename to src-ts/tools/learn/learn-lib/use-certificate-scaling-hook/useCertificateScaling.hook.tsx diff --git a/src-ts/tools/learn/learn.routes.tsx b/src-ts/tools/learn/learn.routes.tsx index e34c6e200..d4bf9f83d 100644 --- a/src-ts/tools/learn/learn.routes.tsx +++ b/src-ts/tools/learn/learn.routes.tsx @@ -15,6 +15,8 @@ const UserCertificate: LazyLoadedComponent = lazyLoad(() => import('./course-cer const FreeCodeCamp: LazyLoadedComponent = lazyLoad(() => import('./free-code-camp'), 'FreeCodeCamp') const MyLearning: LazyLoadedComponent = lazyLoad(() => import('./my-learning'), 'MyLearning') const LandingLearn: LazyLoadedComponent = lazyLoad(() => import('./Learn')) +const MyTCACertificate: LazyLoadedComponent = lazyLoad(() => import('./tca-certificate'), 'MyTCACertificate') +const UserTCACertificate: LazyLoadedComponent = lazyLoad(() => import('./tca-certificate'), 'UserTCACertificate') export enum LEARN_PATHS { certificate = '/certificate', @@ -24,6 +26,7 @@ export enum LEARN_PATHS { fcc = '/learn/fcc', root = '/learn', startCourseRouteFlag = 'start-course', + tcaCertifications = 'tca-certifications', } export const rootRoute: string = LEARN_PATHS.root @@ -74,6 +77,14 @@ export function getUserCertificateSsr( return `${LearnConfig.CERT_DOMAIN}/${handle}/${provider}/${certification}/${encodeURI(title)}` } +export function getUserTCACertificateSsr( + certification: string, + handle: string, + title: string, +): string { + return `${LearnConfig.CERT_DOMAIN}/${handle}/tca/${certification}/${encodeURI(title)}` +} + export function getUserCertificateUrl( provider: string, certification: string, @@ -86,6 +97,10 @@ export function getViewStyleParamKey(): string { return Object.keys(LearnConfig.CERT_ALT_PARAMS)[0] } +export function getTCACertificationPath(certification: string): string { + return `${LEARN_PATHS.root}/${LEARN_PATHS.tcaCertifications}/${certification}` +} + export const learnRoutes: ReadonlyArray = [ { children: [ @@ -137,6 +152,18 @@ export const learnRoutes: ReadonlyArray = [ id: 'My Learning', route: 'my-learning', }, + { + children: [], + element: , + id: 'My TCA Certification', + route: 'tca-certifications/:certification/certificate', + }, + { + children: [], + element: , + id: 'User TCA Certification', + route: 'tca-certifications/:certification/:memberHandle/certificate', + }, ], element: , id: toolTitle, diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.module.scss b/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.module.scss new file mode 100644 index 000000000..d9b0568b9 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.module.scss @@ -0,0 +1,80 @@ +@import '../../../../lib/styles/includes'; + +.wrap { + padding-top: $space-xxxxl; + padding-bottom: calc($space-xxxxl + $space-xs); + flex: 99 1 auto; + display: flex; + + background: $tc-grad15; +} + +.content-wrap { + display: flex; + @include pagePaddings; + margin: auto; + width: 100%; + justify-content: center; + + gap: $space-xxxxl; + + @include ltemd { + flex-direction: column; + margin: 0 auto auto; + } +} + +.btns-wrap { + display: flex; + flex-direction: column; + align-items: center; + + gap: $space-sm; + + &:last-child { + margin-top: auto; + } + + @include ltemd { + flex-direction: row; + &:last-child { + justify-content: center; + } + } +} + +.certificate-wrap { + aspect-ratio: 1.25715; + width: 880px; + + background: #fff; + + box-shadow: 0 20px 36px rgba($tc-black, 0.22); + + &:global(.large-container) { + aspect-ratio: unset; + @include socialPreviewImg; + } +} + +.share-btn:global(.button.icon) { + @include icon-mxx; + border-radius: 50%; + + color: $tc-white; + border: $border solid $tc-white; + + display: flex; + align-items: center; + justify-content: center; + + padding: $space-sm; + + &:hover { + background: transparent; + } + + svg { + @include icon-xxl; + } +} diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.tsx b/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.tsx new file mode 100644 index 000000000..5a0802bf7 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/CertificateView.tsx @@ -0,0 +1,172 @@ +import { FC, MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react' +import { NavigateFunction, useNavigate } from 'react-router-dom' +import classNames from 'classnames' + +import { + FacebookSocialShareBtn, + fileDownloadCanvasAsImage, + IconOutline, + LinkedinSocialShareBtn, + LoadingSpinner, + TwitterSocialShareBtn, + UserProfile, +} from '../../../../lib' +import { + ActionButton, + TCACertificationProviderData, + useCertificateCanvas, + useCertificatePrint, + useCertificateScaling, + useGetTCACertificationMOCK, + useGetUserTCACompletedCertificationsMOCK, + UserCompletedTCACertificationsProviderData, +} from '../../learn-lib' +import { getTCACertificationPath, getUserTCACertificateSsr } from '../../learn.routes' + +import { Certificate } from './certificate' +import styles from './CertificateView.module.scss' + +export type CertificateViewStyle = 'large-container' | undefined + +interface CertificateViewProps { + certification: string, + hideActions?: boolean, + onCertificationNotCompleted: () => void + profile: UserProfile, + viewStyle: CertificateViewStyle +} + +const CertificateView: FC = (props: CertificateViewProps) => { + + const navigate: NavigateFunction = useNavigate() + const tcaCertificationPath: string = getTCACertificationPath(props.certification) + const certificateElRef: MutableRefObject = useRef() + const certificateWrapRef: MutableRefObject = useRef() + + const userName: string = useMemo(() => ( + [props.profile.firstName, props.profile.lastName].filter(Boolean) + .join(' ') + || props.profile.handle + ), [props.profile.firstName, props.profile.handle, props.profile.lastName]) + + const { + certification, + ready: certReady, + }: TCACertificationProviderData = useGetTCACertificationMOCK(props.certification) + + function getCertTitle(user: string): string { + return `${user} - ${certification?.title} Certification` + } + + const certUrl: string = getUserTCACertificateSsr( + props.certification, + props.profile.handle, + getCertTitle(props.profile.handle), + ) + + const certificationTitle: string = getCertTitle(userName || props.profile.handle) + + const { + certifications: [completedCertificate], + ready: completedCertificateReady, + }: UserCompletedTCACertificationsProviderData = useGetUserTCACompletedCertificationsMOCK( + props.profile.userId, + props.certification, + ) + + const hasCompletedTheCertification: boolean = !!completedCertificate + + const ready: boolean = useMemo(() => ( + completedCertificateReady && certReady + ), [completedCertificateReady, certReady]) + + const readyAndCompletedCertification: boolean = useMemo(() => ( + ready && hasCompletedTheCertification + ), [hasCompletedTheCertification, ready]) + + useCertificateScaling(ready ? certificateWrapRef : undefined) + + const handleBackBtnClick: () => void = useCallback(() => { + navigate(tcaCertificationPath) + }, [tcaCertificationPath, navigate]) + + const getCertificateCanvas: () => Promise = useCertificateCanvas(certificateElRef) + + const handleDownload: () => Promise = useCallback(async () => { + + const canvas: HTMLCanvasElement | void = await getCertificateCanvas() + if (!!canvas) { + fileDownloadCanvasAsImage(canvas, `${certificationTitle}.png`) + } + + }, [certificationTitle, getCertificateCanvas]) + + const handlePrint: () => Promise = useCertificatePrint(certificateElRef, certificationTitle) + + useEffect(() => { + if (ready && !hasCompletedTheCertification) { + props.onCertificationNotCompleted() + } + }, [tcaCertificationPath, hasCompletedTheCertification, props, ready]) + + return ( + <> + + + {ready && readyAndCompletedCertification && ( +
+
+ {!props.hideActions && ( +
+ } + onClick={handleBackBtnClick} + /> +
+ )} +
+ +
+ {!props.hideActions && ( +
+ } + onClick={handlePrint} + /> + } + onClick={handleDownload} + /> + + + +
+ )} +
+
+ )} + + ) +} + +export default CertificateView diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.module.scss b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.module.scss new file mode 100644 index 000000000..1ea492c07 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.module.scss @@ -0,0 +1,168 @@ +@import '../../../../../lib/styles/includes'; +@import './includes'; + +.wrap { + display: flex; + height: 100%; +} + +.details { + width: 55%; + padding: calc($space-mx + $space-lg); + display: flex; + flex-direction: column; + flex: 1; + + &-inner { + max-width: 385px; + flex: 1; + display: flex; + flex-direction: column; + } + + .wrap:global(.large-container) & { + padding-left: calc($space-mx + $space-mxx); + } + + h2 { + color: $blue-160; + &:global(.grad) { + @include grad-text-color($tc-grad15); + } + } + + h3 { + font-size: 48px; + line-height: 50px; + color: $tc-black; + margin-top: $space-sm; + } + + h1 { + font-size: 80px; + line-height: 72px; + } + + &:global(.theme-dev) { + .username { + color: $tc-dev-track-color; + } + .username:global(.grad) { + @include grad-text-color($tc-dev-grad); + } + .tc-handle { + color: $turq-140; + } + } + &:global(.theme-design) { + .username { + color: $tc-design-track-color; + } + .username:global(.grad) { + @include grad-text-color($tc-design-grad); + } + .tc-handle { + color: $blue-140; + } + } + &:global(.theme-qa) { + .username { + color: $tc-qa-track-color; + } + .username:global(.grad) { + @include grad-text-color($tc-qa-grad); + } + .tc-handle { + color: $purple-120; + } + } + &:global(.theme-datascience) { + .username { + color: $tc-datascience-track-color; + } + .username:global(.grad) { + @include grad-text-color($tc-datascience-grad); + } + .tc-handle { + color: $red-140; + } + } +} + +.username { + margin-top: calc($space-mx + $space-sm); +} + +.tc-handle { + color: $turq-140; + + margin-top: $space-xl; +} + +.badges { + width: 45%; + position: relative; + z-index: 1; + + padding: 56px; + padding-left: 80px; + + display: flex; + flex-direction: column; + max-width: 500px; +} + +.course-card { + margin: auto; +} + +.pattern-bg { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; +} + +.logos { + margin-top: auto; + display: flex; +} + +.logo { + display: flex; + align-items: center; + height: 52px; + + svg { + width: auto; + height: 100%; + } +} + +.divider { + width: $border; + background: $black-10; + margin: 0 $space-xxxxl; +} + +.vendor { + color: $tc-white; + text-align: right; + + :global(.body-ultra-small) { + line-height: 22px; + } +} + +.vendor-logo { + margin-top: $space-sm; + display: flex; + justify-content: flex-end; + + svg { + display: block; + height: 16px; + } +} diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.tsx b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.tsx new file mode 100644 index 000000000..db3b1e808 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/Certificate.tsx @@ -0,0 +1,86 @@ +import { FC, MutableRefObject } from 'react' +import classNames from 'classnames' + +import { LearnConfig } from '../../../learn-config' +import { LearnCertificateTrackType } from '../../../learn-lib' + +import { CertificateBgPattern } from './certificate-bg-pattern' +import { CourseCard } from './course-card' + +import styles from './Certificate.module.scss' +import { FccLogoSvg, TcAcademyLogoSvg, TcLogoSvg } from '../../../../../lib' + +interface CertificateProps { + completedDate?: string + course?: string + elRef?: MutableRefObject + provider?: string + tcHandle?: string + type?: LearnCertificateTrackType + userName?: string + viewStyle?: 'large-container' +} + +const Certificate: FC = (props: CertificateProps) => { + + const certificateType: LearnCertificateTrackType = props.type ?? 'DEV' + + const elementSelector: { [attr: string]: string } = { + [LearnConfig.CERT_ELEMENT_SELECTOR.attribute]: LearnConfig.CERT_ELEMENT_SELECTOR.value, + } + + return ( +
+
+
+

Topcoder Academy

+

Certificate of Completion

+

+ {props.userName} +

+
+ Topcoder Handle: + {props.tcHandle} +
+
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+ Course content provided by + {' '} + {props.provider} +
+
+ +
+
+
+
+ ) +} + +export default Certificate diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.module.scss b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.module.scss new file mode 100644 index 000000000..02c4c5bec --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.module.scss @@ -0,0 +1,58 @@ +@import '../includes'; + +@mixin wave-bg-pattern($grad) { + background: url('./wave-bg-2.png') 0 0 no-repeat, + url('./pattern-bg.png') bottom right no-repeat, + $grad; + background-size: 12px 100%, 220% 100%, 100% 100%; +} + +.wrap { + width: 100%; + height: 100%; + background: 0 0 no-repeat; + background-size: 100% 100%; + + position: relative; + + &:global(.theme-dev) { + background-image: $tc-dev-grad; + } + &:global(.theme-design) { + background-image: $tc-design-grad; + } + &:global(.theme-qa) { + background-image: $tc-qa-grad; + } + + &:global(.theme-datascience) { + background-image: $tc-datascience-grad; + } + + &:global(.theme-interview) { + background-image: $tc-interview-grad; + } + + &:global(.theme-security) { + background-image: $tc-security-grad; + } + + > div { + position: absolute; + top: 0; + left: -1px; + width: 100%; + height: 100%; + z-index: 1; + + &:global(.pattern-bg) { + background: url('./pattern-bg.png') bottom right no-repeat; + background-size: 220% 100%; + } + + &:global(.wave-bg) { + background: url('./wave-bg.png') 0 0 repeat-y; + background-size: 400px 116px; + } + } +} diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.tsx b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.tsx new file mode 100644 index 000000000..61c86af60 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/CertificateBgPattern.tsx @@ -0,0 +1,19 @@ +import { FC } from 'react' +import classNames from 'classnames' + +import { LearnCertificateTrackType } from '../../../../learn-lib' + +import styles from './CertificateBgPattern.module.scss' + +interface CertificateBgPatternProps { + type: LearnCertificateTrackType +} + +const CertificateBgPattern: FC = (props: CertificateBgPatternProps) => ( +
+
+
+
+) + +export default CertificateBgPattern diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/index.ts b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/index.ts new file mode 100644 index 000000000..68b6d47de --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/index.ts @@ -0,0 +1 @@ +export { default as CertificateBgPattern } from './CertificateBgPattern' diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/pattern-bg.png b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/pattern-bg.png new file mode 100644 index 000000000..dacb6a7ef Binary files /dev/null and b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/pattern-bg.png differ diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/wave-bg.png b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/wave-bg.png new file mode 100644 index 000000000..f7f87b7f2 Binary files /dev/null and b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/certificate-bg-pattern/wave-bg.png differ diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.module.scss b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.module.scss new file mode 100644 index 000000000..f15f09a95 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.module.scss @@ -0,0 +1,52 @@ +@import '../../../../../../lib/styles/includes'; + +.wrap { + border-radius: $space-sm; + width: 100%; + width: 264px; + background: rgba($tc-white, 0.12); + backdrop-filter: blur(160px); + color: $tc-white; + + h5 { + font-size: 24px; + line-height: 26px; + } +} + +.badge { + img { + display: block; + @include icon-size(78); + } +} + +.course-title { + margin-top: $space-lg; +} + +.top-wrap { + background: url(./wave-bg.png) 0 0 no-repeat; + background-size: 264px 600px; + padding: $space-xxl; + min-height: 264px; +} + +.details { + display: flex; + align-items: center; + padding: $space-xxl; + gap: calc($space-sm + $border); + + svg { + @include icon-xl; + } + + span { + line-height: 24px; + + & > span { + display: block; + } + } +} diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.tsx b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.tsx new file mode 100644 index 000000000..aa669788c --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/CourseCard.tsx @@ -0,0 +1,41 @@ +import { FC } from 'react' +import classNames from 'classnames' + +import { IconOutline, textFormatDateLocaleShortString } from '../../../../../../lib' +import { CourseBadge, LearnCertificateTrackType } from '../../../../learn-lib' + +import styles from './CourseCard.module.scss' + +interface CourseCardProps { + completedDate?: string + course?: string + type: LearnCertificateTrackType +} + +const CourseCard: FC = (props: CourseCardProps) => ( +
+
+
+ +
+
+ {props.course} +
+
+
+ + + Completed + + { + props.completedDate && ( + textFormatDateLocaleShortString(new Date(props.completedDate)) + ) + } + + +
+
+) + +export default CourseCard diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/index.ts b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/index.ts new file mode 100644 index 000000000..63d15ecfc --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/index.ts @@ -0,0 +1 @@ +export { default as CourseCard } from './CourseCard' diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/wave-bg.png b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/wave-bg.png new file mode 100644 index 000000000..c23dfb5fd Binary files /dev/null and b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/course-card/wave-bg.png differ diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/includes.scss b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/includes.scss new file mode 100644 index 000000000..6db0d3dd5 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/includes.scss @@ -0,0 +1,17 @@ +$tc-dev-track-color: #048467; +$tc-design-track-color: #065D6E; +$tc-qa-track-color: #363D8C; +$tc-datascience-track-color: #723390; +$tc-dev-grad: linear-gradient(84.92deg, #048467 2.08%, #064871 97.43%); +$tc-design-grad: linear-gradient(84.92deg, #065D6E 2.08%, #06596E 2.09%, #3E3B91 97.43%); +$tc-qa-grad: linear-gradient(84.92deg, #363D8C 2.08%, #723390 97.43%); +$tc-datascience-grad: linear-gradient(84.92deg, #723390 2.08%, #8C384F 97.43%); +$tc-interview-grad: linear-gradient(84.92deg, #048467 2.08%, #064871 33.85%, #6831A8 66.15%, #8C384D 97.43%); +$tc-security-grad: linear-gradient(84.92deg, #048467 2.08%, #064871 97.43%); + +@mixin grad-text-color($grad) { + background: $grad; + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/certificate/index.ts b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/index.ts new file mode 100644 index 000000000..fd27dbdf7 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/certificate/index.ts @@ -0,0 +1 @@ +export { default as Certificate } from './Certificate' diff --git a/src-ts/tools/learn/tca-certificate/certificate-view/index.ts b/src-ts/tools/learn/tca-certificate/certificate-view/index.ts new file mode 100644 index 000000000..37e2894f6 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/certificate-view/index.ts @@ -0,0 +1,2 @@ +export { default as CertificateView } from './CertificateView' +export type { CertificateViewStyle } from './CertificateView' diff --git a/src-ts/tools/learn/tca-certificate/index.ts b/src-ts/tools/learn/tca-certificate/index.ts new file mode 100644 index 000000000..1772df7f1 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/index.ts @@ -0,0 +1,2 @@ +export * from './my-certificate' +export * from './user-certificate' diff --git a/src-ts/tools/learn/tca-certificate/my-certificate/MyTCACertificate.tsx b/src-ts/tools/learn/tca-certificate/my-certificate/MyTCACertificate.tsx new file mode 100644 index 000000000..418883c66 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/my-certificate/MyTCACertificate.tsx @@ -0,0 +1,45 @@ +import { FC, useCallback, useContext, useEffect } from 'react' +import { NavigateFunction, Params, useNavigate, useParams } from 'react-router-dom' + +import { + LoadingSpinner, + profileContext, + ProfileContextData, +} from '../../../../lib' +import { getTCACertificationPath } from '../../learn.routes' +import CertificateView from '../certificate-view/CertificateView' + +const MyTCACertificate: FC<{}> = () => { + const routeParams: Params = useParams() + const { profile, initialized: profileReady }: ProfileContextData = useContext(profileContext) + + const navigate: NavigateFunction = useNavigate() + const certificationParam: string = routeParams.certification ?? '' + const tcaCertificationPath: string = getTCACertificationPath(certificationParam) + + const navigateToCertification: () => void = useCallback(() => { + navigate(tcaCertificationPath) + }, [tcaCertificationPath, navigate]) + + useEffect(() => { + if (profileReady && !profile) { + navigateToCertification() + } + }, [profileReady, profile, navigateToCertification]) + + return ( + <> + + + {profileReady && profile && ( + + )} + + ) +} + +export default MyTCACertificate diff --git a/src-ts/tools/learn/tca-certificate/my-certificate/index.ts b/src-ts/tools/learn/tca-certificate/my-certificate/index.ts new file mode 100644 index 000000000..e6bcb7548 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/my-certificate/index.ts @@ -0,0 +1 @@ +export { default as MyTCACertificate } from './MyTCACertificate' diff --git a/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.module.scss b/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.module.scss new file mode 100644 index 000000000..06821e91a --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.module.scss @@ -0,0 +1,5 @@ +.full-screen-cert { + flex: 1 1 auto; + display: flex; + flex-direction: column; +} \ No newline at end of file diff --git a/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.tsx b/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.tsx new file mode 100644 index 000000000..a7295e901 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/user-certificate/UserTCACertificate.tsx @@ -0,0 +1,78 @@ +import { Dispatch, FC, MutableRefObject, SetStateAction, useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react' +import { NavigateFunction, Params, useNavigate, useParams, useSearchParams } from 'react-router-dom' + +import { + LoadingSpinner, + profileGetPublicAsync, + UserProfile, +} from '../../../../lib' +import { getTCACertificationPath, getViewStyleParamKey } from '../../learn.routes' +import { CertificateView, CertificateViewStyle } from '../certificate-view' + +import styles from './UserTCACertificate.module.scss' + +const UserTCACertificate: FC<{}> = () => { + + const navigate: NavigateFunction = useNavigate() + const wrapElRef: MutableRefObject = useRef() + const routeParams: Params = useParams() + const [queryParams]: [URLSearchParams, any] = useSearchParams() + + const [profile, setProfile]: [ + UserProfile | undefined, + Dispatch> + ] = useState() + const [profileReady, setProfileReady]: [boolean, Dispatch>] = useState(false) + + const certificationParam: string = routeParams.certification ?? '' + + const tcaCertificationPath: string = getTCACertificationPath(certificationParam) + + useEffect(() => { + if (routeParams.memberHandle) { + profileGetPublicAsync(routeParams.memberHandle) + .then(userProfile => { + setProfile(userProfile) + setProfileReady(true) + }) + } + }, [routeParams.memberHandle, setProfileReady]) + + useLayoutEffect(() => { + const el: HTMLElement = wrapElRef.current + if (!el) { + return + } + + [].forEach.call(el.parentElement?.children ?? [], (c: HTMLElement) => { + if (c !== el) { + Object.assign(c.style, { display: 'none' }) + } + }) + el.classList.add(styles['full-screen-cert']) + }) + + const navigateToCertification: () => void = useCallback(() => { + navigate(tcaCertificationPath) + }, [tcaCertificationPath, navigate]) + + return ( + <> + + + {profileReady && profile && ( +
+ +
+ )} + + ) +} + +export default UserTCACertificate diff --git a/src-ts/tools/learn/tca-certificate/user-certificate/index.ts b/src-ts/tools/learn/tca-certificate/user-certificate/index.ts new file mode 100644 index 000000000..58bd97b52 --- /dev/null +++ b/src-ts/tools/learn/tca-certificate/user-certificate/index.ts @@ -0,0 +1 @@ +export { default as UserTCACertificate } from './UserTCACertificate'