Skip to content

Commit 26ef81c

Browse files
authored
Merge pull request #1020 from topcoder-platform/diazz-code-30376331
Topcoder Admin App - Permission Management
2 parents 1462b63 + 871c4aa commit 26ef81c

File tree

109 files changed

+7061
-176
lines changed

Some content is hidden

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

109 files changed

+7061
-176
lines changed

src/apps/admin/src/AdminApp.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Outlet, Routes } from 'react-router-dom'
33

44
import { routerContext, RouterContextData } from '~/libs/core'
55

6-
import { Layout, SWRConfigProvider } from './lib'
6+
import { AdminAppContextProvider, Layout, SWRConfigProvider } from './lib'
77
import { toolTitle } from './admin-app.routes'
88
import './lib/styles/index.scss'
99

@@ -24,12 +24,14 @@ const AdminApp: FC = () => {
2424

2525
return (
2626
<div>
27-
<SWRConfigProvider>
28-
<Layout>
29-
<Outlet />
30-
<Routes>{childRoutes}</Routes>
31-
</Layout>
32-
</SWRConfigProvider>
27+
<AdminAppContextProvider>
28+
<SWRConfigProvider>
29+
<Layout>
30+
<Outlet />
31+
<Routes>{childRoutes}</Routes>
32+
</Layout>
33+
</SWRConfigProvider>
34+
</AdminAppContextProvider>
3335
</div>
3436
)
3537
}

src/apps/admin/src/admin-app.routes.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
import {
1111
manageChallengeRouteId,
1212
manageReviewRouteId,
13+
permissionManagementRouteId,
1314
rootRoute,
1415
userManagementRouteId,
1516
} from './config/routes.config'
@@ -42,6 +43,33 @@ const ManageReviewerPage: LazyLoadedComponent = lazyLoad(
4243
() => import('./review-management/ManageReviewerPage'),
4344
'ManageReviewerPage',
4445
)
46+
const PermissionManagement: LazyLoadedComponent = lazyLoad(
47+
() => import('./permission-management/PermissionManagement'),
48+
)
49+
const PermissionRolesPage: LazyLoadedComponent = lazyLoad(
50+
() => import('./permission-management/PermissionRolesPage'),
51+
'PermissionRolesPage',
52+
)
53+
const PermissionRoleMembersPage: LazyLoadedComponent = lazyLoad(
54+
() => import('./permission-management/PermissionRoleMembersPage'),
55+
'PermissionRoleMembersPage',
56+
)
57+
const PermissionAddRoleMembersPage: LazyLoadedComponent = lazyLoad(
58+
() => import('./permission-management/PermissionAddRoleMembersPage'),
59+
'PermissionAddRoleMembersPage',
60+
)
61+
const PermissionGroupsPage: LazyLoadedComponent = lazyLoad(
62+
() => import('./permission-management/PermissionGroupsPage'),
63+
'PermissionGroupsPage',
64+
)
65+
const PermissionGroupMembersPage: LazyLoadedComponent = lazyLoad(
66+
() => import('./permission-management/PermissionGroupMembersPage'),
67+
'PermissionGroupMembersPage',
68+
)
69+
const PermissionAddGroupMembersPage: LazyLoadedComponent = lazyLoad(
70+
() => import('./permission-management/PermissionAddGroupMembersPage'),
71+
'PermissionAddGroupMembersPage',
72+
)
4573

4674
export const toolTitle: string = ToolTitle.admin
4775

@@ -96,6 +124,44 @@ export const adminRoutes: ReadonlyArray<PlatformRoute> = [
96124
id: manageReviewRouteId,
97125
route: manageReviewRouteId,
98126
},
127+
// Permission Management Module
128+
{
129+
children: [
130+
{
131+
element: <PermissionRolesPage />,
132+
id: 'permission-roles-page',
133+
route: 'roles',
134+
},
135+
{
136+
element: <PermissionRoleMembersPage />,
137+
id: 'permission-role-members-page',
138+
route: 'roles/:roleId/role-members',
139+
},
140+
{
141+
element: <PermissionAddRoleMembersPage />,
142+
id: 'permission-add-role-members-page',
143+
route: 'roles/:roleId/role-members/add',
144+
},
145+
{
146+
element: <PermissionGroupsPage />,
147+
id: 'permission-groups-page',
148+
route: 'groups',
149+
},
150+
{
151+
element: <PermissionGroupMembersPage />,
152+
id: 'permission-group-members-page',
153+
route: 'groups/:groupId/group-members',
154+
},
155+
{
156+
element: <PermissionAddGroupMembersPage />,
157+
id: 'permission-add-group-members-page',
158+
route: 'groups/:groupId/group-members/add',
159+
},
160+
],
161+
element: <PermissionManagement />,
162+
id: permissionManagementRouteId,
163+
route: permissionManagementRouteId,
164+
},
99165
],
100166
domain: AppSubdomain.admin,
101167
element: <AdminApp />,

src/apps/admin/src/config/routes.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export const rootRoute: string
1111
export const manageChallengeRouteId = 'challenge-management'
1212
export const manageReviewRouteId = 'review-management'
1313
export const userManagementRouteId = 'user-management'
14+
export const permissionManagementRouteId = 'permission-management'
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
gap: 20px;
5+
position: relative;
6+
}
7+
8+
.actionButtons {
9+
display: flex;
10+
justify-content: flex-end;
11+
gap: 6px;
12+
}
13+
14+
.blockRadios {
15+
display: flex;
16+
flex-direction: column;
17+
gap: 18px;
18+
}
19+
20+
.dialogLoadingSpinnerContainer {
21+
position: absolute;
22+
width: 64px;
23+
display: flex;
24+
align-items: center;
25+
justify-content: center;
26+
bottom: 0;
27+
height: 64px;
28+
left: 0;
29+
30+
.spinner {
31+
background: none;
32+
}
33+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/**
2+
* Dialog add group.
3+
*/
4+
import { FC, useCallback } from 'react'
5+
import {
6+
Controller,
7+
ControllerRenderProps,
8+
useForm,
9+
UseFormReturn,
10+
} from 'react-hook-form'
11+
import _ from 'lodash'
12+
import classNames from 'classnames'
13+
14+
import {
15+
BaseModal,
16+
Button,
17+
InputCheckbox,
18+
InputText,
19+
InputTextarea,
20+
LoadingSpinner,
21+
} from '~/libs/ui'
22+
import { yupResolver } from '@hookform/resolvers/yup'
23+
24+
import { FormAddGroup } from '../../models'
25+
import { formAddGroupSchema } from '../../utils'
26+
27+
import styles from './DialogAddGroup.module.scss'
28+
29+
interface Props {
30+
className?: string
31+
open: boolean
32+
setOpen: (isOpen: boolean) => void
33+
onSubmitForm?: (filter: FormAddGroup) => void
34+
isLoading?: boolean
35+
}
36+
37+
export const DialogAddGroup: FC<Props> = (props: Props) => {
38+
const handleClose = useCallback(() => {
39+
if (!props.isLoading) {
40+
props.setOpen(false)
41+
}
42+
// eslint-disable-next-line react-hooks/exhaustive-deps
43+
}, [props.isLoading])
44+
const {
45+
register,
46+
handleSubmit,
47+
control,
48+
formState: { errors, isValid },
49+
}: UseFormReturn<FormAddGroup> = useForm({
50+
defaultValues: {
51+
description: '',
52+
name: '',
53+
privateGroup: false,
54+
selfRegister: false,
55+
},
56+
mode: 'all',
57+
resolver: yupResolver(formAddGroupSchema),
58+
})
59+
const onSubmit = useCallback(
60+
(data: FormAddGroup) => {
61+
props.onSubmitForm?.(data)
62+
},
63+
// eslint-disable-next-line react-hooks/exhaustive-deps
64+
[props.onSubmitForm],
65+
)
66+
67+
return (
68+
<BaseModal
69+
allowBodyScroll
70+
blockScroll
71+
focusTrapped={false}
72+
title='Add Group'
73+
onClose={handleClose}
74+
open={props.open}
75+
classNames={{
76+
modal: classNames(styles.modal),
77+
}}
78+
>
79+
<form
80+
className={classNames(styles.container, props.className)}
81+
onSubmit={handleSubmit(onSubmit)}
82+
>
83+
<div>
84+
<InputText
85+
type='text'
86+
name='name'
87+
label='Name'
88+
placeholder='Enter'
89+
tabIndex={0}
90+
forceUpdateValue
91+
onChange={_.noop}
92+
error={_.get(errors, 'name.message')}
93+
inputControl={register('name')}
94+
dirty
95+
disabled={props.isLoading}
96+
/>
97+
<InputTextarea
98+
name='description'
99+
label='Description'
100+
placeholder='Enter'
101+
tabIndex={0}
102+
onChange={_.noop}
103+
inputControl={register('description')}
104+
dirty
105+
disabled={props.isLoading}
106+
/>
107+
<div className={styles.blockRadios}>
108+
<Controller
109+
name='privateGroup'
110+
control={control}
111+
render={function render(controlProps: {
112+
field: ControllerRenderProps<
113+
FormAddGroup,
114+
'privateGroup'
115+
>
116+
}) {
117+
return (
118+
<InputCheckbox
119+
name='privateGroup'
120+
label='Private Group'
121+
onChange={controlProps.field.onChange}
122+
checked={controlProps.field.value}
123+
disabled={props.isLoading}
124+
/>
125+
)
126+
}}
127+
/>
128+
<Controller
129+
name='selfRegister'
130+
control={control}
131+
render={function render(controlProps: {
132+
field: ControllerRenderProps<
133+
FormAddGroup,
134+
'selfRegister'
135+
>
136+
}) {
137+
return (
138+
<InputCheckbox
139+
name='selfRegister'
140+
label='Self Register'
141+
onChange={controlProps.field.onChange}
142+
checked={controlProps.field.value}
143+
disabled={props.isLoading}
144+
/>
145+
)
146+
}}
147+
/>
148+
</div>
149+
</div>
150+
<div className={styles.actionButtons}>
151+
<Button
152+
secondary
153+
size='lg'
154+
onClick={handleClose}
155+
disabled={props.isLoading}
156+
>
157+
Close
158+
</Button>
159+
<Button
160+
type='submit'
161+
primary
162+
size='lg'
163+
disabled={props.isLoading || !isValid}
164+
>
165+
Save
166+
</Button>
167+
</div>
168+
169+
{props.isLoading && (
170+
<div className={styles.dialogLoadingSpinnerContainer}>
171+
<LoadingSpinner className={styles.spinner} />
172+
</div>
173+
)}
174+
</form>
175+
</BaseModal>
176+
)
177+
}
178+
179+
export default DialogAddGroup
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as DialogAddGroup } from './DialogAddGroup'
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.container {
4+
display: flex;
5+
flex-direction: column;
6+
padding: $sp-8 $sp-8 0;
7+
8+
@include ltelg {
9+
padding: $sp-4 $sp-4 0;
10+
}
11+
}
12+
13+
.fields {
14+
display: grid;
15+
grid-template-columns: 1fr 1fr;
16+
gap: 15px 30px;
17+
18+
@include ltemd {
19+
grid-template-columns: 1fr;
20+
}
21+
}
22+
23+
.blockBottom {
24+
display: flex;
25+
justify-content: flex-end;
26+
align-items: flex-start;
27+
gap: 30px;
28+
flex-wrap: wrap;
29+
30+
@include ltemd {
31+
flex-direction: column;
32+
align-items: flex-end;
33+
}
34+
}

0 commit comments

Comments
 (0)