Skip to content

Commit 37a7313

Browse files
authored
Merge pull request #1026 from topcoder-platform/diazz-code-30376498
Topcoder Admin App - Resource Management on Challenge Management
2 parents cd7e42f + 96f2e65 commit 37a7313

Some content is hidden

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

57 files changed

+1833
-24
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ const ManageUserPage: LazyLoadedComponent = lazyLoad(
2929
() => import('./challenge-management/ManageUserPage'),
3030
'ManageUserPage',
3131
)
32+
const ManageResourcePage: LazyLoadedComponent = lazyLoad(
33+
() => import('./challenge-management/ManageResourcePage'),
34+
'ManageResourcePage',
35+
)
36+
const AddResourcePage: LazyLoadedComponent = lazyLoad(
37+
() => import('./challenge-management/AddResourcePage'),
38+
'AddResourcePage',
39+
)
3240
const UserManagementPage: LazyLoadedComponent = lazyLoad(
3341
() => import('./user-management/UserManagementPage'),
3442
'UserManagementPage',
@@ -127,6 +135,16 @@ export const adminRoutes: ReadonlyArray<PlatformRoute> = [
127135
id: 'manage-user',
128136
route: ':challengeId/manage-user',
129137
},
138+
{
139+
element: <ManageResourcePage />,
140+
id: 'manage-resource',
141+
route: ':challengeId/manage-resource',
142+
},
143+
{
144+
element: <AddResourcePage />,
145+
id: 'add-resource',
146+
route: ':challengeId/manage-resource/add',
147+
},
130148
],
131149
element: <ChallengeManagement />,
132150
id: manageChallengeRouteId,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@import '@libs/ui/styles/includes';
2+
3+
.container {
4+
display: flex;
5+
flex-direction: column;
6+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/**
2+
* Add Resource Page.
3+
*/
4+
import { FC, useCallback, useContext } from 'react'
5+
import {
6+
Controller,
7+
ControllerRenderProps,
8+
useForm,
9+
UseFormReturn,
10+
} from 'react-hook-form'
11+
import { NavigateFunction, useNavigate, useParams } from 'react-router-dom'
12+
import _ from 'lodash'
13+
import classNames from 'classnames'
14+
15+
import { Button, LinkButton } from '~/libs/ui'
16+
import { yupResolver } from '@hookform/resolvers/yup'
17+
18+
import {
19+
useManageAddChallengeResource,
20+
useManageAddChallengeResourceProps,
21+
useOnComponentDidMount,
22+
useSearchUserInfo,
23+
useSearchUserInfoProps,
24+
} from '../../lib/hooks'
25+
import {
26+
ChallengeManagementContext,
27+
ChallengeManagementContextType,
28+
FieldHandleSelect,
29+
FieldSingleSelect,
30+
FormAddWrapper,
31+
InputTextAdmin,
32+
PageWrapper,
33+
} from '../../lib'
34+
import { FormAddResource, SelectOption } from '../../lib/models'
35+
import { formAddResourceSchema } from '../../lib/utils'
36+
37+
import styles from './AddResourcePage.module.scss'
38+
39+
interface Props {
40+
className?: string
41+
}
42+
43+
export const AddResourcePage: FC<Props> = (props: Props) => {
44+
const { challengeId = '' }: { challengeId?: string } = useParams<{
45+
challengeId: string
46+
}>()
47+
48+
const { isLoading, doSearchUserInfo, setUserInfo }: useSearchUserInfoProps
49+
= useSearchUserInfo()
50+
51+
const { resourceRoles, loadResourceRoles, resourceRolesLoading }: ChallengeManagementContextType
52+
= useContext(ChallengeManagementContext)
53+
54+
const {
55+
doAddChallengeResource,
56+
isAdding,
57+
}: useManageAddChallengeResourceProps = useManageAddChallengeResource(challengeId)
58+
59+
const navigate: NavigateFunction = useNavigate()
60+
const {
61+
control,
62+
handleSubmit,
63+
register,
64+
formState: { errors, isDirty },
65+
setValue,
66+
}: UseFormReturn<FormAddResource> = useForm({
67+
defaultValues: {
68+
handle: undefined,
69+
resourceRole: undefined,
70+
userId: '',
71+
},
72+
mode: 'all',
73+
resolver: yupResolver(formAddResourceSchema),
74+
})
75+
const onSubmit = useCallback((data: FormAddResource) => {
76+
doAddChallengeResource(data, () => {
77+
navigate('./..')
78+
})
79+
// eslint-disable-next-line react-hooks/exhaustive-deps
80+
}, [])
81+
82+
useOnComponentDidMount(() => {
83+
loadResourceRoles()
84+
})
85+
86+
return (
87+
<PageWrapper
88+
pageTitle='Add Resource'
89+
className={classNames(styles.container, props.className)}
90+
>
91+
<FormAddWrapper
92+
onSubmit={handleSubmit(onSubmit)}
93+
isAdding={isAdding}
94+
actions={(
95+
<>
96+
<Button
97+
primary
98+
size='lg'
99+
type='submit'
100+
disabled={isAdding || isLoading || !isDirty}
101+
>
102+
Add Resource
103+
</Button>
104+
<LinkButton
105+
disabled={isAdding}
106+
secondary
107+
to='./..'
108+
size='lg'
109+
>
110+
Cancel
111+
</LinkButton>
112+
</>
113+
)}
114+
>
115+
<InputTextAdmin
116+
type='text'
117+
name='userId'
118+
label='User ID (Unfocus this field to fetch user handle)'
119+
placeholder='Enter'
120+
tabIndex={0}
121+
onChange={_.noop}
122+
classNameWrapper={styles.field}
123+
inputControl={register('userId', {
124+
onBlur: e => {
125+
doSearchUserInfo(
126+
e.target.value,
127+
userInfo => {
128+
setValue(
129+
'handle',
130+
{
131+
label: userInfo.handle,
132+
value: userInfo.userId,
133+
},
134+
{
135+
shouldValidate: true,
136+
},
137+
)
138+
},
139+
() => {
140+
// eslint-disable-next-line unicorn/no-null
141+
setValue('handle', null as any, { // only null value work in this place
142+
shouldValidate: true,
143+
})
144+
},
145+
)
146+
},
147+
})}
148+
disabled={isAdding}
149+
error={_.get(errors, 'userId.message')}
150+
dirty
151+
isLoading={isLoading}
152+
/>
153+
<Controller
154+
name='handle'
155+
control={control}
156+
render={function render(controlProps: {
157+
field: ControllerRenderProps<FormAddResource, 'handle'>
158+
}) {
159+
return (
160+
<FieldHandleSelect
161+
label='Handle'
162+
value={controlProps.field.value}
163+
onChange={function onChange(result: SelectOption) {
164+
controlProps.field.onChange(result)
165+
const userId = `${result.value}`
166+
setUserInfo({
167+
handle: result.label as string,
168+
userId,
169+
})
170+
setValue('userId', userId, {
171+
shouldValidate: true,
172+
})
173+
}}
174+
onBlur={controlProps.field.onBlur}
175+
error={_.get(errors, 'handle.message')}
176+
dirty
177+
isLoading={isLoading}
178+
disabled={isAdding}
179+
/>
180+
)
181+
}}
182+
/>
183+
<Controller
184+
name='resourceRole'
185+
control={control}
186+
render={function render(controlProps: {
187+
field: ControllerRenderProps<
188+
FormAddResource,
189+
'resourceRole'
190+
>
191+
}) {
192+
return (
193+
<FieldSingleSelect
194+
options={resourceRoles.map(resourceRole => ({
195+
label: resourceRole.name,
196+
value: resourceRole.id,
197+
}))}
198+
label='Resource Role'
199+
placeholder='Select'
200+
value={controlProps.field.value}
201+
onChange={controlProps.field.onChange}
202+
onBlur={controlProps.field.onBlur}
203+
error={_.get(errors, 'resourceRole.message')}
204+
dirty
205+
disabled={isAdding}
206+
isLoading={resourceRolesLoading}
207+
/>
208+
)
209+
}}
210+
/>
211+
</FormAddWrapper>
212+
</PageWrapper>
213+
)
214+
}
215+
216+
export default AddResourcePage
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as AddResourcePage } from './AddResourcePage'
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.container {
2+
display: flex;
3+
flex-direction: column;
4+
}
5+
6+
.blockTableContainer {
7+
position: relative;
8+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Manage Resource Page.
3+
*/
4+
import { FC } from 'react'
5+
import { useParams } from 'react-router-dom'
6+
import classNames from 'classnames'
7+
8+
import { LinkButton } from '~/libs/ui'
9+
import { PlusIcon } from '@heroicons/react/solid'
10+
11+
import {
12+
useManageChallengeResources,
13+
useManageChallengeResourcesProps,
14+
} from '../../lib/hooks'
15+
import {
16+
ActionLoading,
17+
PageWrapper,
18+
ResourceTable,
19+
TableLoading,
20+
TableNoRecord,
21+
} from '../../lib'
22+
23+
import styles from './ManageResourcePage.module.scss'
24+
25+
interface Props {
26+
className?: string
27+
}
28+
29+
export const ManageResourcePage: FC<Props> = (props: Props) => {
30+
const { challengeId = '' }: { challengeId?: string } = useParams<{
31+
challengeId: string
32+
}>()
33+
34+
const {
35+
isLoading,
36+
resources,
37+
totalPages,
38+
page,
39+
setPage,
40+
sort,
41+
setSort,
42+
isRemovingBool,
43+
doRemoveResource,
44+
}: useManageChallengeResourcesProps = useManageChallengeResources(
45+
challengeId,
46+
{
47+
createdString: 'created',
48+
},
49+
)
50+
51+
return (
52+
<PageWrapper
53+
pageTitle='Resource Management'
54+
className={classNames(styles.container, props.className)}
55+
headerActions={(
56+
<>
57+
<LinkButton
58+
primary
59+
size='lg'
60+
to='add'
61+
icon={PlusIcon}
62+
iconToLeft
63+
label='add resource'
64+
/>
65+
<LinkButton primary light to='./../..' size='lg'>
66+
Back
67+
</LinkButton>
68+
</>
69+
)}
70+
>
71+
{isLoading ? (
72+
<TableLoading />
73+
) : (
74+
<>
75+
{resources.length === 0 ? (
76+
<TableNoRecord />
77+
) : (
78+
<div className={styles.blockTableContainer}>
79+
<ResourceTable
80+
datas={resources}
81+
totalPages={totalPages}
82+
page={page}
83+
setPage={setPage}
84+
setSort={setSort}
85+
sort={sort}
86+
isRemovingBool={isRemovingBool}
87+
doRemoveItem={doRemoveResource}
88+
/>
89+
90+
{isRemovingBool && <ActionLoading />}
91+
</div>
92+
)}
93+
</>
94+
)}
95+
</PageWrapper>
96+
)
97+
}
98+
99+
export default ManageResourcePage
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as ManageResourcePage } from './ManageResourcePage'

0 commit comments

Comments
 (0)