Skip to content

Commit 7565194

Browse files
committed
TCA-407 - add filter & sort for my-learnings page
1 parent 9c4aa74 commit 7565194

File tree

18 files changed

+479
-85
lines changed

18 files changed

+479
-85
lines changed

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

Lines changed: 1 addition & 1 deletion
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
}
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/tools/learn/my-learning/MyLearning.module.scss

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,6 @@
2121
}
2222
}
2323

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-
6824
.loading-spinner {
6925
background: none;
70-
}
26+
}

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

Lines changed: 28 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} />
@@ -64,41 +87,7 @@ const MyLearning: FC<{}> = () => {
6487
</div>
6588
</Portal>
6689

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-
)}
90+
{ready && renderTabs()}
10291
</div>
10392
</ContentLayout>
10493
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
@include ltemd {
13+
> * {
14+
flex: 1 1 0;
15+
}
16+
}
17+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { FC, ReactNode } from 'react'
2+
3+
import { LearnCertification, MyCourseCompletedCard, UserCertificationCompleted } from '../../learn-lib'
4+
import { sortOptions } from '../my-learning-sort-options'
5+
import { MyTabsViews } from '../my-tabs-navbar'
6+
import { TabContentLayout } from '../tab-content-layout'
7+
import { useSortAndFilter, UseSortAndFilterValue } from '../use-sort-and-filter'
8+
9+
import styles from './CompletedTab.module.scss'
10+
11+
interface CompletedTabProps {
12+
allCertificates: ReadonlyArray<LearnCertification>
13+
certificatesById: {[key: string]: LearnCertification}
14+
certifications: ReadonlyArray<UserCertificationCompleted>
15+
}
16+
17+
const CompletedTab: FC<CompletedTabProps> = (props: CompletedTabProps) => {
18+
19+
const {
20+
handleCategoryChange,
21+
certifications,
22+
handleSortChange,
23+
}: UseSortAndFilterValue = useSortAndFilter(
24+
props.allCertificates,
25+
props.certifications
26+
)
27+
28+
const renderCertificationsList: () => ReactNode = () => (
29+
certifications.map((certif) => (
30+
<MyCourseCompletedCard
31+
certification={props.certificatesById[certif.certificationId]}
32+
key={certif.certificationId}
33+
completed={certif.completedDate}
34+
/>
35+
))
36+
)
37+
38+
return (
39+
<TabContentLayout
40+
certifications={props.allCertificates}
41+
title={MyTabsViews.completed}
42+
sortOptions={sortOptions.completed}
43+
onSortChange={handleSortChange}
44+
onCategoryChange={handleCategoryChange}
45+
>
46+
<div className={styles['cards-wrap']}>
47+
{renderCertificationsList()}
48+
</div>
49+
</TabContentLayout>
50+
)
51+
}
52+
53+
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'
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { FC, ReactNode } from 'react'
2+
3+
import { LearnCertification, MyCourseInProgressCard, UserCertificationInProgress } from '../../learn-lib'
4+
import { sortOptions } from '../my-learning-sort-options'
5+
import { MyTabsViews } from '../my-tabs-navbar'
6+
import { TabContentLayout } from '../tab-content-layout'
7+
import { useSortAndFilter, UseSortAndFilterValue } from '../use-sort-and-filter'
8+
9+
interface InProgressTabProps {
10+
allCertificates: ReadonlyArray<LearnCertification>
11+
certificatesById: {[key: string]: LearnCertification}
12+
certifications: ReadonlyArray<UserCertificationInProgress>
13+
}
14+
15+
const InProgressTab: FC<InProgressTabProps> = (props: InProgressTabProps) => {
16+
17+
const {
18+
handleCategoryChange,
19+
certifications,
20+
handleSortChange,
21+
}: UseSortAndFilterValue = useSortAndFilter(
22+
props.allCertificates,
23+
props.certifications
24+
)
25+
26+
const renderCertificationsList: () => ReactNode = () => (
27+
certifications.map((certif) => (
28+
<MyCourseInProgressCard
29+
certification={props.certificatesById[certif.certificationId]}
30+
key={certif.certificationId}
31+
theme='detailed'
32+
currentLesson={certif.currentLesson}
33+
completedPercentage={certif.courseProgressPercentage / 100}
34+
startDate={certif.startDate}
35+
/>
36+
))
37+
)
38+
39+
return (
40+
<TabContentLayout
41+
certifications={props.allCertificates}
42+
title={MyTabsViews.inProgress}
43+
sortOptions={sortOptions.inProgress}
44+
onSortChange={handleSortChange}
45+
onCategoryChange={handleCategoryChange}
46+
>
47+
{renderCertificationsList()}
48+
</TabContentLayout>
49+
)
50+
}
51+
52+
export default InProgressTab
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as InProgressTab } from './InProgressTab'
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export interface SortOption {
2+
label: string,
3+
value: string,
4+
}
5+
6+
const COMPLETED_SORT_OPTIONS: ReadonlyArray<SortOption> = [
7+
{label: 'Completion date: new to old', value: '-completedDate'},
8+
{label: 'Completion date: old to new', value: 'completedDate'},
9+
]
10+
11+
const IN_PROGRESS_SORT_OPTIONS: ReadonlyArray<SortOption> = [
12+
{label: 'Recent activity: new to old', value: '-updatedAt'},
13+
{label: 'Recent activity: old to new', value: 'updatedAt'},
14+
{label: 'Most progress', value: '-courseProgressPercentage'},
15+
{label: 'Least progress', value: 'courseProgressPercentage'},
16+
{label: 'Name', value: 'certification'},
17+
// {label: 'Length', field: (c: ), direction: 'asc'},
18+
]
19+
20+
export const sortOptions: {
21+
completed: ReadonlyArray<SortOption>,
22+
inProgress: ReadonlyArray<SortOption>
23+
} = {
24+
completed: COMPLETED_SORT_OPTIONS,
25+
inProgress: IN_PROGRESS_SORT_OPTIONS,
26+
}

0 commit comments

Comments
 (0)