Skip to content

Commit d82d2cd

Browse files
authored
Merge pull request #367 from topcoder-platform/TCA-407_my-learning-filtering
TCA-407 my learning filtering -> dev
2 parents 1e28ab5 + 09c7bdf commit d82d2cd

File tree

22 files changed

+577
-92
lines changed

22 files changed

+577
-92
lines changed

src-ts/lib/form/form-groups/form-input/input-select/InputSelect.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ interface InputSelectProps {
3030
readonly label?: string
3131
readonly name: string
3232
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void
33-
readonly options: Array<InputSelectOption>
33+
readonly options: ReadonlyArray<InputSelectOption>
3434
readonly tabIndex?: number
3535
readonly value?: string
3636
}
@@ -68,7 +68,7 @@ const InputSelect: FC<InputSelectProps> = (props: InputSelectProps) => {
6868
hideInlineErrors={props.hideInlineErrors}
6969
ref={triggerRef}
7070
>
71-
<div className={styles['selected']} onClick={toggleMenu}>
71+
<div className={styles['selected']} onClick={() => !props.disabled && toggleMenu}>
7272
<span className='body-small'>{selectedOption ? label(selectedOption) : ''}</span>
7373
<span className={styles['selected-icon']}>
7474
<IconOutline.ChevronDownIcon />
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './InputSelect'
12
export { default as InputSelect } from './InputSelect'

src-ts/lib/form/form-groups/form-input/input-wrapper/InputWrapper.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ $error-line-height: 14px;
7878
background-color: $black-10;
7979
background: $black-10;
8080
border-color: $black-40;
81+
pointer-events: none;
8182
}
8283

8384
&.input-error {

src-ts/tools/learn/learn-lib/my-course-card/completed/Completed.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import classNames from 'classnames'
12
import { FC } from 'react'
23

34
import { Button, textFormatDateLocaleShortString } from '../../../../../lib'
@@ -18,7 +19,7 @@ const Completed: FC<CompletedProps> = (props: CompletedProps) => {
1819
}
1920

2021
return (
21-
<div className={styles['wrap']}>
22+
<div className={classNames(styles['wrap'], 'course-card-wrap', 'completed')}>
2223
<div className={styles['line']}>
2324
<CourseTitle
2425
title={props.certification.title}

src-ts/tools/learn/learn-lib/my-course-card/in-progress/InProgress.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const InProgress: FC<InProgressProps> = (props: InProgressProps) => {
5050
}
5151

5252
return (
53-
<div className={classNames(styles['wrap'], styles['large'])}>
53+
<div className={classNames(styles['wrap'], styles['large'], 'course-card-wrap', 'in-progress')}>
5454
<div className={styles['inner']}>
5555
<div className={styles['line']}>
5656
<CourseTitle

src-ts/tools/learn/my-learning/MyLearning.module.scss

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
.wrap {
44
flex: 1 1 auto;
55
position: relative;
6-
}
7-
8-
.content-layout {
9-
background: $black-5;
6+
margin-top: $space-xxxxl;
7+
display: flex;
8+
flex-direction: column;
109
}
1110

1211
.hero-wrap {
@@ -21,50 +20,6 @@
2120
}
2221
}
2322

24-
.title-line {
25-
display: flex;
26-
align-items: center;
27-
28-
svg {
29-
@include icon-size(60);
30-
margin-right: $space-sm;
31-
@include ltemd {
32-
@include icon-size(38);
33-
}
34-
}
35-
}
36-
37-
.courses-area {
38-
padding: $space-xxxxl 0;
39-
gap: $space-lg;
40-
display: flex;
41-
flex-direction: column;
42-
position: relative;
43-
&:empty {
44-
display: none;
45-
}
46-
47-
@include ltemd {
48-
gap: $space-xxl;
49-
}
50-
}
51-
52-
.cards-wrap {
53-
display: flex;
54-
gap: $space-xxl;
55-
flex-wrap: wrap;
56-
57-
> * {
58-
flex: 0 1 calc(50% - calc($space-xxl / 2));
59-
}
60-
61-
@include ltemd {
62-
> * {
63-
flex: 1 1 0;
64-
}
65-
}
66-
}
67-
6823
.loading-spinner {
6924
background: none;
70-
}
25+
}

src-ts/tools/learn/my-learning/MyLearning.tsx

Lines changed: 29 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import { FC, useContext, useMemo } from 'react'
1+
import { Dispatch, FC, ReactNode, SetStateAction, useContext, useMemo, useState } from 'react'
22

33
import { Breadcrumb, BreadcrumbItemModel, ContentLayout, LoadingSpinner, Portal, profileContext, ProfileContextData } from '../../../lib'
44
import {
55
AllCertificationsProviderData,
66
LearnCertification,
7-
LearningHat,
8-
MyCourseCompletedCard,
9-
MyCourseInProgressCard,
107
useAllCertifications,
118
useLearnBreadcrumb,
129
UserCertificationsProviderData,
@@ -15,7 +12,10 @@ import {
1512
} from '../learn-lib'
1613
import { LEARN_PATHS } from '../learn.routes'
1714

15+
import { CompletedTab } from './completed-tab'
1816
import { HeroCard } from './hero-card'
17+
import { InProgressTab } from './in-progress-tab'
18+
import { MyTabsNavbar, MyTabsViews } from './my-tabs-navbar'
1919
import styles from './MyLearning.module.scss'
2020

2121
interface CertificatesByIdType {
@@ -27,6 +27,7 @@ const MyLearning: FC<{}> = () => {
2727
const { profile, initialized: profileReady }: ProfileContextData = useContext(profileContext)
2828
const { completed, inProgress, ready: coursesReady }: UserCertificationsProviderData = useUserCertifications()
2929
const { certifications, ready: certificatesReady }: AllCertificationsProviderData = useAllCertifications()
30+
const [activeTab, setActiveTab]: [MyTabsViews|undefined, Dispatch<SetStateAction<MyTabsViews|undefined>>] = useState()
3031

3132
const ready: boolean = profileReady && coursesReady && certificatesReady
3233

@@ -44,6 +45,28 @@ const MyLearning: FC<{}> = () => {
4445
},
4546
])
4647

48+
const renderTabs: () => ReactNode = () => (
49+
<MyTabsNavbar
50+
inProgress={inProgress.length}
51+
completed={completed.length}
52+
onTabChange={setActiveTab}
53+
>
54+
{activeTab === MyTabsViews.completed ? (
55+
<CompletedTab
56+
allCertificates={certifications}
57+
certificatesById={certificatesById}
58+
certifications={completed}
59+
/>
60+
) : (
61+
<InProgressTab
62+
allCertificates={certifications}
63+
certificatesById={certificatesById}
64+
certifications={inProgress}
65+
/>
66+
)}
67+
</MyTabsNavbar>
68+
)
69+
4770
return (
4871
<ContentLayout contentClass={styles['content-layout']}>
4972
<Breadcrumb items={breadcrumb} />
@@ -54,6 +77,7 @@ const MyLearning: FC<{}> = () => {
5477
<div className={styles['hero-wrap']}>
5578
<WaveHero
5679
title='my learning'
80+
theme='light'
5781
text={`
5882
This is your very own page to keep track of your professional education and skill building.
5983
From here you can resume your courses in progress or review past accomplishments.
@@ -64,41 +88,7 @@ const MyLearning: FC<{}> = () => {
6488
</div>
6589
</Portal>
6690

67-
{ready && (
68-
<>
69-
<div className={styles['courses-area']}>
70-
{inProgress.map((certif) => (
71-
<MyCourseInProgressCard
72-
certification={certificatesById[certif.certificationId]}
73-
key={certif.certificationId}
74-
theme='detailed'
75-
currentLesson={certif.currentLesson}
76-
completedPercentage={certif.courseProgressPercentage / 100}
77-
startDate={certif.startDate}
78-
/>
79-
))}
80-
</div>
81-
82-
{!!completed.length && (
83-
<div className={styles['courses-area']}>
84-
<div className={styles['title-line']}>
85-
<LearningHat />
86-
<h2 className='details'>Completed Courses</h2>
87-
</div>
88-
89-
<div className={styles['cards-wrap']}>
90-
{completed.map((certif) => (
91-
<MyCourseCompletedCard
92-
certification={certificatesById[certif.certificationId]}
93-
key={certif.certificationId}
94-
completed={certif.completedDate}
95-
/>
96-
))}
97-
</div>
98-
</div>
99-
)}
100-
</>
101-
)}
91+
{ready && renderTabs()}
10292
</div>
10393
</ContentLayout>
10494
)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@import '../../../../lib/styles/includes';
2+
3+
.cards-wrap {
4+
display: flex;
5+
gap: $space-xxl;
6+
flex-wrap: wrap;
7+
8+
> * {
9+
flex: 0 1 calc(50% - calc($space-xxl / 2));
10+
}
11+
12+
> :global(.course-card-wrap) {
13+
background: $black-5;
14+
}
15+
16+
@include ltemd {
17+
> * {
18+
flex: 1 1 0;
19+
}
20+
}
21+
}
22+
23+
.placeholder-wrap {
24+
flex: 1 1 auto;
25+
display: flex;
26+
flex-direction: column;
27+
align-items: center;
28+
justify-content: center;
29+
gap: $space-xxl;
30+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { FC, ReactNode } from 'react'
2+
3+
import { Button } from '../../../../lib'
4+
import { LearnCertification, MyCourseCompletedCard, UserCertificationCompleted } from '../../learn-lib'
5+
import { LEARN_PATHS } from '../../learn.routes'
6+
import { sortOptions } from '../my-learning-sort-options'
7+
import { MyTabsViews } from '../my-tabs-navbar'
8+
import { TabContentLayout } from '../tab-content-layout'
9+
import { useSortAndFilter, UseSortAndFilterValue } from '../use-sort-and-filter'
10+
11+
import styles from './CompletedTab.module.scss'
12+
13+
interface CompletedTabProps {
14+
allCertificates: ReadonlyArray<LearnCertification>
15+
certificatesById: {[key: string]: LearnCertification}
16+
certifications: ReadonlyArray<UserCertificationCompleted>
17+
}
18+
19+
const CompletedTab: FC<CompletedTabProps> = (props: CompletedTabProps) => {
20+
21+
const {
22+
handleCategoryChange,
23+
certifications,
24+
handleSortChange,
25+
}: UseSortAndFilterValue = useSortAndFilter(
26+
props.allCertificates,
27+
props.certifications
28+
)
29+
30+
const hasCertifications: boolean = certifications.length >= 1
31+
32+
const renderPlaceholder: () => ReactNode = () => (
33+
<div className={styles['placeholder-wrap']}>
34+
<div className='body-medium-bold'>
35+
Your Completed courses will live here. Let’s go!
36+
</div>
37+
<Button
38+
route={LEARN_PATHS.root}
39+
buttonStyle='primary'
40+
size='md'
41+
label='Start a course'
42+
/>
43+
</div>
44+
)
45+
46+
const renderCertificationsList: () => ReactNode = () => (
47+
hasCertifications ? certifications.map((certif) => (
48+
<MyCourseCompletedCard
49+
certification={props.certificatesById[certif.certificationId]}
50+
key={certif.certificationId}
51+
completed={certif.completedDate}
52+
/>
53+
)) : renderPlaceholder()
54+
)
55+
56+
return (
57+
<TabContentLayout
58+
certifications={props.allCertificates}
59+
title={MyTabsViews.completed}
60+
sortOptions={sortOptions.completed}
61+
onSortChange={handleSortChange}
62+
onCategoryChange={handleCategoryChange}
63+
disableFilters={!hasCertifications}
64+
>
65+
<div className={styles['cards-wrap']}>
66+
{renderCertificationsList()}
67+
</div>
68+
</TabContentLayout>
69+
)
70+
}
71+
72+
export default CompletedTab
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as CompletedTab } from './CompletedTab'

0 commit comments

Comments
 (0)