Skip to content

Commit b8266df

Browse files
authored
Merge pull request #960 from topcoder-platform/dev
RELEASE: TSJR-57 skills manager
2 parents 6c27bb9 + 99cbe32 commit b8266df

File tree

103 files changed

+3324
-65
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+3324
-65
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ workflows:
257257
branches:
258258
only:
259259
- dev
260+
- TSJR-314_skill-manager_landing-page
260261

261262
- deployQa:
262263
context: org-global

.vscode/components.code-snippets

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// ],
1616
// "description": "Log output to console"
1717
// }
18-
"[MFE] React component": {
18+
"[PLAT] React component": {
1919
"scope": "typescript,typescriptreact",
2020
"prefix": "rfc",
2121
"body": [
@@ -28,18 +28,39 @@
2828
"",
2929
"const ${1:ComponentName}: FC<${1:ComponentName}Props> = props => {",
3030
"",
31-
" return (",
31+
" return (",
32+
" <div className={styles.wrap}>",
33+
" </div>",
34+
" )",
35+
"}",
36+
"",
37+
"export default ${1:ComponentName}",
38+
""
39+
],
40+
"description": "Create a react functional component"
41+
},
42+
"[PLAT] Simple React component": {
43+
"scope": "typescript,typescriptreact",
44+
"prefix": "rfc",
45+
"body": [
46+
"import { FC } from 'react'",
47+
"",
48+
"import styles from './${1:ComponentName}.module.scss'",
49+
"",
50+
"interface ${1:ComponentName}Props {",
51+
"}",
52+
"",
53+
"const ${1:ComponentName}: FC<${1:ComponentName}Props> = props => (",
3254
" <div className={styles.wrap}>",
3355
" </div>",
34-
" )",
35-
"}",
56+
")",
3657
"",
3758
"export default ${1:ComponentName}",
3859
""
3960
],
4061
"description": "Create a react functional component"
4162
},
42-
"[MFE] export comp": {
63+
"[PLAT] export comp": {
4364
"scope": "typescript,typescriptreact",
4465
"prefix": "exp",
4566
"body": [
@@ -48,14 +69,21 @@
4869
],
4970
"description": "Export module"
5071
},
51-
"[MFE] use state": {
72+
"[PLAT] use state": {
5273
"scope": "typescript,typescriptreact",
5374
"prefix": "usest",
5475
"body": [
5576
"const [$1, set$2]: [$3, Dispatch<SetStateAction<$3>>] = useState($4)$0",
5677
]
5778
},
58-
"[MFE] Storybook Template": {
79+
"[PLAT] includes": {
80+
"scope": "css,scss",
81+
"prefix": "includes",
82+
"body": [
83+
"@import '@libs/ui/styles/includes';",
84+
]
85+
},
86+
"[PLAT] Storybook Template": {
5987
"scope": "typescript,typescriptreact",
6088
"prefix": "sb",
6189
"body": [

src/apps/admin/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Admin App

src/apps/admin/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src'

src/apps/admin/src/AdminApp.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { FC, useContext } from 'react'
2+
import { Outlet, Routes } from 'react-router-dom'
3+
4+
import { routerContext, RouterContextData } from '~/libs/core'
5+
import { SharedSwrConfig } from '~/libs/shared'
6+
7+
import { toolTitle } from './admin.routes'
8+
9+
const AdminApp: FC<{}> = () => {
10+
const { getChildRoutes }: RouterContextData = useContext(routerContext)
11+
12+
return (
13+
<SharedSwrConfig>
14+
<Outlet />
15+
<Routes>
16+
{getChildRoutes(toolTitle)}
17+
</Routes>
18+
</SharedSwrConfig>
19+
)
20+
}
21+
22+
export default AdminApp

src/apps/admin/src/admin.routes.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Navigate } from 'react-router-dom'
2+
3+
import { lazyLoad, LazyLoadedComponent, PlatformRoute, UserRole } from '~/libs/core'
4+
import { AppSubdomain, EnvironmentConfig, ToolTitle } from '~/config'
5+
6+
import { skillsManagerRootRoute, skillsManagerRoutes } from './skills-manager'
7+
8+
const AdminApp: LazyLoadedComponent = lazyLoad(() => import('./AdminApp'))
9+
10+
export const rootRoute: string = (
11+
EnvironmentConfig.SUBDOMAIN === AppSubdomain.admin ? '' : `/${AppSubdomain.admin}`
12+
)
13+
14+
export const toolTitle: string = ToolTitle.admin
15+
export const absoluteRootRoute: string = `${window.location.origin}${rootRoute}`
16+
17+
export const adminRoutes: ReadonlyArray<PlatformRoute> = [
18+
{
19+
authRequired: true,
20+
children: [
21+
...skillsManagerRoutes,
22+
{
23+
element: <Navigate to={`${rootRoute}${skillsManagerRootRoute}`} />,
24+
id: 'Default Admin Route',
25+
route: '',
26+
},
27+
],
28+
domain: AppSubdomain.admin,
29+
element: <AdminApp />,
30+
id: toolTitle,
31+
rolesRequired: [UserRole.administrator],
32+
route: rootRoute,
33+
},
34+
]

src/apps/admin/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export {
2+
adminRoutes,
3+
rootRoute as adminRootRoute,
4+
} from './admin.routes'
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { FC, useContext } from 'react'
2+
import { Outlet, Routes } from 'react-router-dom'
3+
4+
import { routerContext, RouterContextData } from '~/libs/core'
5+
6+
import { skillsManagerRoutes } from './skills-manager.routes'
7+
import { SkillsManagerContext } from './context'
8+
9+
const SkillsManager: FC<{}> = () => {
10+
const { getRouteElement }: RouterContextData = useContext(routerContext)
11+
12+
return (
13+
<SkillsManagerContext>
14+
<Outlet />
15+
<Routes>
16+
{skillsManagerRoutes.map(getRouteElement)}
17+
</Routes>
18+
</SkillsManagerContext>
19+
)
20+
}
21+
22+
export default SkillsManager
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import '@libs/ui/styles/includes';
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import {
2+
Children,
3+
cloneElement,
4+
FC,
5+
isValidElement,
6+
ReactNode,
7+
useCallback,
8+
useEffect,
9+
useRef,
10+
useState,
11+
} from 'react'
12+
13+
import { AccordionItemProps } from './accordion-item'
14+
import styles from './Accordion.module.scss'
15+
16+
interface AccordionProps {
17+
children: JSX.Element[] | JSX.Element
18+
defaultOpen?: boolean
19+
}
20+
21+
function computeOpenSectionsState(props: AccordionProps): {[key: string]: boolean} {
22+
const newOpenState: {[key: string]: boolean} = {}
23+
24+
Children.forEach<ReactNode>(props.children, child => {
25+
if (!isValidElement(child)) {
26+
return
27+
}
28+
29+
const childKey = child.key as string
30+
newOpenState[childKey] = child.props.open ?? props.defaultOpen
31+
})
32+
33+
return newOpenState
34+
}
35+
36+
const Accordion: FC<AccordionProps> = props => {
37+
const prevProps = useRef({ ...props })
38+
const [openedSections, setOpenedSections] = useState<{[key: string]: boolean}>({})
39+
40+
const handleToggle = useCallback((key: string) => {
41+
setOpenedSections(all => ({ ...all, [key]: !all[key] }))
42+
}, [])
43+
44+
// check if props have changed and update the openedSections synchronously
45+
if (prevProps.current.defaultOpen !== props.defaultOpen) {
46+
prevProps.current = { ...props }
47+
Object.assign(openedSections, computeOpenSectionsState(props))
48+
}
49+
50+
// use an effect to make sure the changes are propagated in the state
51+
useEffect(() => {
52+
setOpenedSections(computeOpenSectionsState(props))
53+
// eslint-disable-next-line react-hooks/exhaustive-deps
54+
}, [props.defaultOpen])
55+
56+
const renderAccordions = (children: JSX.Element[] | JSX.Element): ReactNode => (
57+
Children.map<ReactNode, ReactNode>(children, child => {
58+
if (isValidElement(child)) {
59+
const childKey = child.key as string
60+
openedSections[childKey] = openedSections[childKey] ?? child.props.open ?? props.defaultOpen
61+
62+
return cloneElement(
63+
child,
64+
{
65+
open: !!openedSections[childKey],
66+
toggle: function toggle() { handleToggle(childKey) },
67+
} as AccordionItemProps,
68+
)
69+
}
70+
71+
return child
72+
})
73+
)
74+
75+
return (
76+
<div className={styles.wrap}>
77+
{renderAccordions(props.children)}
78+
</div>
79+
)
80+
}
81+
82+
export default Accordion

0 commit comments

Comments
 (0)