Skip to content

Commit 99844c2

Browse files
authored
Merge pull request #304 from topcoder-platform/gamification
Gamification Admin UI QA merge
2 parents 2e8c595 + 1175617 commit 99844c2

35 files changed

+573
-81
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
"redux-thunk": "^2.4.1",
6161
"sass": "^1.49.8",
6262
"styled-components": "^5.3.5",
63+
"swr": "^1.3.0",
6364
"tc-auth-lib": "topcoder-platform/tc-auth-lib#1.0.4",
6465
"typescript": "^4.6.3",
6566
"uuid": "^8.3.2"

src-ts/lib/global-config.model.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ export interface GlobalConfig {
1111
}
1212
DISABLED_TOOLS?: Array<string>
1313
ENV: string
14+
GAMIFICATION: {
15+
ORG_ID: string
16+
},
1417
LOGGING: {
1518
PUBLIC_TOKEN: string
1619
SERVICE: string

src-ts/lib/pagination/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
1+
export * from './infinite-page-dao.model'
2+
export * from './infinite-page-handler.model'
13
export * from './page.model'
24
export * from './sort.model'
5+
export * from './use-infinite-page.hook'
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface InfinitePageDao<T> {
2+
count: number
3+
// TODO: rename this 'items' so it can be used in a grid/card view
4+
rows: ReadonlyArray<T>
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export interface InfinitePageHandler<T> {
2+
data?: ReadonlyArray<T>
3+
getAndSetNext: () => void
4+
hasMore: boolean
5+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { flatten, map } from 'lodash'
2+
// tslint:disable-next-line: no-submodule-imports
3+
import useSWRInfinite, { SWRInfiniteResponse } from 'swr/infinite'
4+
5+
import { InfinitePageDao } from './infinite-page-dao.model'
6+
import { InfinitePageHandler } from './infinite-page-handler.model'
7+
8+
export function useGetInfinitePage<T>(getKey: (index: number, previousPageData: InfinitePageDao<T>) => string | undefined):
9+
InfinitePageHandler<T> {
10+
11+
const { data, setSize, size }: SWRInfiniteResponse<InfinitePageDao<T>> = useSWRInfinite(getKey, { revalidateFirstPage: false })
12+
13+
// flatten version of badges paginated data
14+
const outputData: ReadonlyArray<T> = flatten(map(data, dao => dao.rows))
15+
16+
function getAndSetNext(): void {
17+
setSize(size + 1)
18+
}
19+
20+
return {
21+
data: outputData,
22+
getAndSetNext,
23+
hasMore: outputData.length < (data?.[0]?.count || 0),
24+
}
25+
}

src-ts/lib/table/Table.module.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@
6161
margin-right: -29px;
6262
}
6363
}
64+
65+
&.centerHeader {
66+
justify-content: center;
67+
}
6468
}
6569

6670
.tooltip {
@@ -90,3 +94,8 @@
9094
.tootlipBody {
9195
min-width: 200px;
9296
}
97+
98+
.loadBtnWrap {
99+
display: flex;
100+
justify-content: center;
101+
}

src-ts/lib/table/Table.tsx

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import classNames from 'classnames'
22
import { Dispatch, MouseEvent, SetStateAction, useEffect, useState } from 'react'
33

4+
import { Button } from '../button'
45
import { Sort } from '../pagination'
56
import '../styles/_includes.scss'
67
import { IconOutline } from '../svgs'
@@ -15,7 +16,10 @@ import styles from './Table.module.scss'
1516
interface TableProps<T> {
1617
readonly columns: ReadonlyArray<TableColumn<T>>
1718
readonly data: ReadonlyArray<T>
19+
readonly moreToLoad?: boolean
20+
readonly onLoadMoreClick?: () => void
1821
readonly onRowClick?: (data: T) => void
22+
readonly onToggleSort?: (sort: Sort) => void
1923
}
2024

2125
interface DefaultSortDirectionMap {
@@ -34,7 +38,7 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
3438
Dispatch<SetStateAction<DefaultSortDirectionMap | undefined>>
3539
]
3640
= useState<DefaultSortDirectionMap | undefined>()
37-
const [sortedData, setSortData]: [ReadonlyArray<T>, Dispatch<SetStateAction<ReadonlyArray<T>>>]
41+
const [sortedData, setSortedData]: [ReadonlyArray<T>, Dispatch<SetStateAction<ReadonlyArray<T>>>]
3842
= useState<ReadonlyArray<T>>(props.data)
3943

4044
useEffect(() => {
@@ -47,7 +51,11 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
4751
setDefaultSortDirectionMap(map)
4852
}
4953

50-
setSortData(tableGetSorted(data, columns, sort))
54+
// if we have a sort handler, don't worry about getting the sorted data;
55+
// otherwise, get the sorted data for the table
56+
const sorted: ReadonlyArray<T> = !!props.onToggleSort ? data : tableGetSorted(data, columns, sort)
57+
58+
setSortedData(sorted)
5159
},
5260
[
5361
columns,
@@ -75,6 +83,9 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
7583
fieldName,
7684
}
7785
setSort(newSort)
86+
87+
// call the callback to notify parent for sort update
88+
props.onToggleSort?.(newSort)
7889
}
7990

8091
const headerRow: Array<JSX.Element> = props.columns
@@ -136,7 +147,7 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
136147
// return the entire row
137148
return (
138149
<tr
139-
className={classNames(styles.tr, !!onRowClick ? styles.clickable : undefined)}
150+
className={classNames(styles.tr, props.onRowClick ? styles.clickable : undefined)}
140151
onClick={onRowClick}
141152
key={index}
142153
>
@@ -158,6 +169,18 @@ const Table: <T extends { [propertyName: string]: any }>(props: TableProps<T>) =
158169
{rowCells}
159170
</tbody>
160171
</table>
172+
{
173+
!!props.moreToLoad && !!props.onLoadMoreClick && (
174+
<div className={styles['loadBtnWrap']}>
175+
<Button
176+
buttonStyle='tertiary'
177+
label='Load More'
178+
size='lg'
179+
onClick={props.onLoadMoreClick}
180+
/>
181+
</div>
182+
)
183+
}
161184
</div>
162185
)
163186
}

src-ts/lib/table/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './table-column.model'
2+
export { tableGetDefaultSort } from './table-functions'
23
export { default as Table } from './Table'

src-ts/lib/table/table-functions/table.functions.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import { Sort } from '../../pagination'
22
import { TableColumn } from '../table-column.model'
33

4-
export function getDefaultSort<T>(columns: ReadonlyArray<TableColumn<T>>): Sort | undefined {
4+
export function getDefaultSort<T>(columns: ReadonlyArray<TableColumn<T>>): Sort {
55

66
const defaultSortColumn: TableColumn<T> | undefined = columns.find(col => col.isDefaultSort)
77
|| columns.find(col => !!col.propertyName)
8+
|| columns?.[0]
89

9-
const defaultSort: Sort | undefined = !defaultSortColumn?.propertyName
10-
? undefined
11-
: {
12-
direction: defaultSortColumn.defaultSortDirection || 'asc',
13-
fieldName: defaultSortColumn.propertyName,
14-
}
10+
// if we didn't find a default sort, we have a problem
11+
if (!defaultSortColumn) {
12+
throw new Error('A table must have at least one column.')
13+
}
14+
15+
const defaultSort: Sort = {
16+
direction: defaultSortColumn.defaultSortDirection || 'asc',
17+
fieldName: defaultSortColumn.propertyName || '',
18+
}
1519

1620
return defaultSort
1721
}

0 commit comments

Comments
 (0)