From a8a6f0ab5e44e94c01b318aee33f39429c8fab29 Mon Sep 17 00:00:00 2001 From: dat Date: Tue, 10 Jun 2025 17:55:36 +0700 Subject: [PATCH 1/2] Topcoder Admin App - Misc Update 0610 --- src/apps/admin/src/admin-app.routes.tsx | 18 -- .../AddResourcePage.module.scss | 6 - .../AddResourcePage/AddResourcePage.tsx | 216 -------------- .../AddResourcePage/index.ts | 1 - .../ManageResourcePage.module.scss | 8 - .../ManageResourcePage/ManageResourcePage.tsx | 99 ------- .../ManageResourcePage/index.ts | 1 - .../ChallengeList/ChallengeList.tsx | 8 - .../DialogAddGroup/DialogAddGroup.tsx | 20 -- .../ResourceTable/ResourceTable.module.scss | 10 - .../ResourceTable/ResourceTable.tsx | 179 ------------ .../src/lib/components/ResourceTable/index.ts | 1 - src/apps/admin/src/lib/components/index.ts | 1 - .../ChallengeManagementContextProvider.tsx | 3 +- src/apps/admin/src/lib/hooks/index.ts | 2 - .../hooks/useManageAddChallengeResource.ts | 118 -------- .../lib/hooks/useManageChallengeResources.ts | 269 ------------------ .../lib/hooks/useManagePermissionGroups.ts | 33 ++- .../src/lib/hooks/useManageUserGroups.ts | 2 +- .../src/lib/models/FormAddResource.model.ts | 10 - .../admin/src/lib/models/Submission.model.ts | 9 +- src/apps/admin/src/lib/models/index.ts | 1 - .../admin/src/lib/services/groups.service.ts | 12 +- src/apps/admin/src/lib/utils/validation.ts | 28 -- 24 files changed, 44 insertions(+), 1011 deletions(-) delete mode 100644 src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.module.scss delete mode 100644 src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.tsx delete mode 100644 src/apps/admin/src/challenge-management/AddResourcePage/index.ts delete mode 100644 src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.module.scss delete mode 100644 src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.tsx delete mode 100644 src/apps/admin/src/challenge-management/ManageResourcePage/index.ts delete mode 100644 src/apps/admin/src/lib/components/ResourceTable/ResourceTable.module.scss delete mode 100644 src/apps/admin/src/lib/components/ResourceTable/ResourceTable.tsx delete mode 100644 src/apps/admin/src/lib/components/ResourceTable/index.ts delete mode 100644 src/apps/admin/src/lib/hooks/useManageAddChallengeResource.ts delete mode 100644 src/apps/admin/src/lib/hooks/useManageChallengeResources.ts delete mode 100644 src/apps/admin/src/lib/models/FormAddResource.model.ts diff --git a/src/apps/admin/src/admin-app.routes.tsx b/src/apps/admin/src/admin-app.routes.tsx index f58fd2fb3..a6f0023dd 100644 --- a/src/apps/admin/src/admin-app.routes.tsx +++ b/src/apps/admin/src/admin-app.routes.tsx @@ -29,18 +29,10 @@ const ManageUserPage: LazyLoadedComponent = lazyLoad( () => import('./challenge-management/ManageUserPage'), 'ManageUserPage', ) -const ManageResourcePage: LazyLoadedComponent = lazyLoad( - () => import('./challenge-management/ManageResourcePage'), - 'ManageResourcePage', -) const ManageSubmissionPage: LazyLoadedComponent = lazyLoad( () => import('./challenge-management/ManageSubmissionPage'), 'ManageSubmissionPage', ) -const AddResourcePage: LazyLoadedComponent = lazyLoad( - () => import('./challenge-management/AddResourcePage'), - 'AddResourcePage', -) const UserManagementPage: LazyLoadedComponent = lazyLoad( () => import('./user-management/UserManagementPage'), 'UserManagementPage', @@ -139,16 +131,6 @@ export const adminRoutes: ReadonlyArray = [ id: 'manage-user', route: ':challengeId/manage-user', }, - { - element: , - id: 'manage-resource', - route: ':challengeId/manage-resource', - }, - { - element: , - id: 'add-resource', - route: ':challengeId/manage-resource/add', - }, { element: , id: 'manage-resource', diff --git a/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.module.scss b/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.module.scss deleted file mode 100644 index 776aec7da..000000000 --- a/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import '@libs/ui/styles/includes'; - -.container { - display: flex; - flex-direction: column; -} diff --git a/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.tsx b/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.tsx deleted file mode 100644 index f030f04ca..000000000 --- a/src/apps/admin/src/challenge-management/AddResourcePage/AddResourcePage.tsx +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Add Resource Page. - */ -import { FC, useCallback, useContext } from 'react' -import { - Controller, - ControllerRenderProps, - useForm, - UseFormReturn, -} from 'react-hook-form' -import { NavigateFunction, useNavigate, useParams } from 'react-router-dom' -import _ from 'lodash' -import classNames from 'classnames' - -import { Button, LinkButton } from '~/libs/ui' -import { yupResolver } from '@hookform/resolvers/yup' - -import { - useManageAddChallengeResource, - useManageAddChallengeResourceProps, - useOnComponentDidMount, - useSearchUserInfo, - useSearchUserInfoProps, -} from '../../lib/hooks' -import { - ChallengeManagementContext, - ChallengeManagementContextType, - FieldHandleSelect, - FieldSingleSelect, - FormAddWrapper, - InputTextAdmin, - PageWrapper, -} from '../../lib' -import { FormAddResource, SelectOption } from '../../lib/models' -import { formAddResourceSchema } from '../../lib/utils' - -import styles from './AddResourcePage.module.scss' - -interface Props { - className?: string -} - -export const AddResourcePage: FC = (props: Props) => { - const { challengeId = '' }: { challengeId?: string } = useParams<{ - challengeId: string - }>() - - const { isLoading, doSearchUserInfo, setUserInfo }: useSearchUserInfoProps - = useSearchUserInfo() - - const { resourceRoles, loadResourceRoles, resourceRolesLoading }: ChallengeManagementContextType - = useContext(ChallengeManagementContext) - - const { - doAddChallengeResource, - isAdding, - }: useManageAddChallengeResourceProps = useManageAddChallengeResource(challengeId) - - const navigate: NavigateFunction = useNavigate() - const { - control, - handleSubmit, - register, - formState: { errors, isDirty }, - setValue, - }: UseFormReturn = useForm({ - defaultValues: { - handle: undefined, - resourceRole: undefined, - userId: '', - }, - mode: 'all', - resolver: yupResolver(formAddResourceSchema), - }) - const onSubmit = useCallback((data: FormAddResource) => { - doAddChallengeResource(data, () => { - navigate('./..') - }) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - useOnComponentDidMount(() => { - loadResourceRoles() - }) - - return ( - - - - - Cancel - - - )} - > - { - doSearchUserInfo( - e.target.value, - userInfo => { - setValue( - 'handle', - { - label: userInfo.handle, - value: userInfo.userId, - }, - { - shouldValidate: true, - }, - ) - }, - () => { - // eslint-disable-next-line unicorn/no-null - setValue('handle', null as any, { // only null value work in this place - shouldValidate: true, - }) - }, - ) - }, - })} - disabled={isAdding} - error={_.get(errors, 'userId.message')} - dirty - isLoading={isLoading} - /> - - }) { - return ( - - ) - }} - /> - - }) { - return ( - ({ - label: resourceRole.name, - value: resourceRole.id, - }))} - label='Resource Role' - placeholder='Select' - value={controlProps.field.value} - onChange={controlProps.field.onChange} - onBlur={controlProps.field.onBlur} - error={_.get(errors, 'resourceRole.message')} - dirty - disabled={isAdding} - isLoading={resourceRolesLoading} - /> - ) - }} - /> - - - ) -} - -export default AddResourcePage diff --git a/src/apps/admin/src/challenge-management/AddResourcePage/index.ts b/src/apps/admin/src/challenge-management/AddResourcePage/index.ts deleted file mode 100644 index 7e4a35404..000000000 --- a/src/apps/admin/src/challenge-management/AddResourcePage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AddResourcePage } from './AddResourcePage' diff --git a/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.module.scss b/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.module.scss deleted file mode 100644 index 06ebfb150..000000000 --- a/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.module.scss +++ /dev/null @@ -1,8 +0,0 @@ -.container { - display: flex; - flex-direction: column; -} - -.blockTableContainer { - position: relative; -} diff --git a/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.tsx b/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.tsx deleted file mode 100644 index bc485739d..000000000 --- a/src/apps/admin/src/challenge-management/ManageResourcePage/ManageResourcePage.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Manage Resource Page. - */ -import { FC } from 'react' -import { useParams } from 'react-router-dom' -import classNames from 'classnames' - -import { LinkButton } from '~/libs/ui' -import { PlusIcon } from '@heroicons/react/solid' - -import { - useManageChallengeResources, - useManageChallengeResourcesProps, -} from '../../lib/hooks' -import { - ActionLoading, - PageWrapper, - ResourceTable, - TableLoading, - TableNoRecord, -} from '../../lib' - -import styles from './ManageResourcePage.module.scss' - -interface Props { - className?: string -} - -export const ManageResourcePage: FC = (props: Props) => { - const { challengeId = '' }: { challengeId?: string } = useParams<{ - challengeId: string - }>() - - const { - isLoading, - resources, - totalPages, - page, - setPage, - sort, - setSort, - isRemovingBool, - doRemoveResource, - }: useManageChallengeResourcesProps = useManageChallengeResources( - challengeId, - { - createdString: 'created', - }, - ) - - return ( - - - - Back - - - )} - > - {isLoading ? ( - - ) : ( - <> - {resources.length === 0 ? ( - - ) : ( -
- - - {isRemovingBool && } -
- )} - - )} -
- ) -} - -export default ManageResourcePage diff --git a/src/apps/admin/src/challenge-management/ManageResourcePage/index.ts b/src/apps/admin/src/challenge-management/ManageResourcePage/index.ts deleted file mode 100644 index 38d7bc603..000000000 --- a/src/apps/admin/src/challenge-management/ManageResourcePage/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ManageResourcePage } from './ManageResourcePage' diff --git a/src/apps/admin/src/lib/components/ChallengeList/ChallengeList.tsx b/src/apps/admin/src/lib/components/ChallengeList/ChallengeList.tsx index 168238588..7e20829f9 100644 --- a/src/apps/admin/src/lib/components/ChallengeList/ChallengeList.tsx +++ b/src/apps/admin/src/lib/components/ChallengeList/ChallengeList.tsx @@ -202,14 +202,6 @@ const Actions: FC<{ > Users -
  • - Resources -
  • {isMM && (
  • = (props: Props) => { ) }} /> - - }) { - return ( - - ) - }} - />
    diff --git a/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.module.scss b/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.module.scss deleted file mode 100644 index e6694ebaf..000000000 --- a/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.module.scss +++ /dev/null @@ -1,10 +0,0 @@ -.container { - display: flex; - flex-direction: column; -} - -.desktopTable { - td { - vertical-align: middle; - } -} diff --git a/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.tsx b/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.tsx deleted file mode 100644 index c2dfa243b..000000000 --- a/src/apps/admin/src/lib/components/ResourceTable/ResourceTable.tsx +++ /dev/null @@ -1,179 +0,0 @@ -/** - * Resource Table. - */ -import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react' -import classNames from 'classnames' - -import { Sort } from '~/apps/gamification-admin/src/game-lib' -import { useWindowSize, WindowSize } from '~/libs/shared' -import { Button, Table, TableColumn } from '~/libs/ui' - -import { ConfirmModal } from '../common/ConfirmModal' -import { MobileTableColumn } from '../../models/MobileTableColumn.model' -import { ChallengeResource } from '../../models' -import { Pagination } from '../common/Pagination' -import { TableMobile } from '../common/TableMobile' -import { TableWrapper } from '../common/TableWrapper' - -import styles from './ResourceTable.module.scss' - -interface Props { - className?: string - isRemovingBool: boolean - datas: ChallengeResource[] - totalPages: number - page: number - setPage: Dispatch> - sort: Sort | undefined - setSort: Dispatch> - doRemoveItem: (item: ChallengeResource) => void -} - -export const ResourceTable: FC = (props: Props) => { - const { width: screenWidth }: WindowSize = useWindowSize() - const isTablet = useMemo(() => screenWidth <= 984, [screenWidth]) - const [showConfirmDialog, setShowConfirmDialog] = useState() - - const columns = useMemo[]>( - () => [ - { - className: 'blockCellWrap', - label: 'Resource ID', - renderer: (data: ChallengeResource) => {data.id}, - type: 'element', - }, - { - label: 'User Handle', - renderer: (data: ChallengeResource) => ( - {data.memberHandle} - ), - type: 'element', - }, - { - className: 'blockCellNoWrap', - label: 'User ID', - renderer: (data: ChallengeResource) => ( - {data.memberId} - ), - type: 'element', - }, - { - className: 'blockCellWrap', - label: 'Resource Role', - renderer: (data: ChallengeResource) => ( - {data.roleId} - ), - type: 'element', - }, - { - label: 'Created Date', - propertyName: 'createdString', - type: 'text', - }, - { - className: 'blockCellWrap', - label: 'Created By', - renderer: (data: ChallengeResource) => ( - {data.createdBy} - ), - type: 'element', - }, - { - label: 'Action', - renderer: (data: ChallengeResource) => ( - - ), - type: 'element', - }, - ], - // eslint-disable-next-line react-hooks/exhaustive-deps - [props.isRemovingBool, props.doRemoveItem], - ) - - const columnsMobile = useMemo[][]>( - () => columns.map(column => { - if (column.label === 'Action') { - return [ - { - ...column, - colSpan: 2, - mobileType: 'last-value', - }, - ] - } - - return [ - { - ...column, - className: '', - label: `${column.label as string} label`, - mobileType: 'label', - renderer: () => ( -
    - {column.label as string} - : -
    - ), - type: 'element', - }, - { - ...column, - mobileType: 'last-value', - }, - ] - }), - [columns], - ) - - return ( - - {isTablet ? ( - - ) : ( - - )} - - - {showConfirmDialog ? ( - -
    - Are you sure you want to remove this resource? -
    -
    - ) : undefined} - - ) -} - -export default ResourceTable diff --git a/src/apps/admin/src/lib/components/ResourceTable/index.ts b/src/apps/admin/src/lib/components/ResourceTable/index.ts deleted file mode 100644 index d6be1187d..000000000 --- a/src/apps/admin/src/lib/components/ResourceTable/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as ResourceTable } from './ResourceTable' diff --git a/src/apps/admin/src/lib/components/index.ts b/src/apps/admin/src/lib/components/index.ts index f739efa1d..1af953242 100644 --- a/src/apps/admin/src/lib/components/index.ts +++ b/src/apps/admin/src/lib/components/index.ts @@ -20,7 +20,6 @@ export * from './ChallengeAddUserDialog' export * from './ReviewSummaryList' export * from './ReviewerList' export * from './RejectPendingConfirmDialog' -export * from './ResourceTable' export * from './FieldHandleSelect' export * from './FieldSingleSelect' export * from './SubmissionTable' diff --git a/src/apps/admin/src/lib/contexts/ChallengeManagementContextProvider.tsx b/src/apps/admin/src/lib/contexts/ChallengeManagementContextProvider.tsx index cda8515f3..5be733b29 100644 --- a/src/apps/admin/src/lib/contexts/ChallengeManagementContextProvider.tsx +++ b/src/apps/admin/src/lib/contexts/ChallengeManagementContextProvider.tsx @@ -7,6 +7,7 @@ import { useMemo, useState, } from 'react' +import _ from 'lodash' import { ChallengeStatus, @@ -75,7 +76,7 @@ export const ChallengeManagementContextProvider: FC = props = setResourceRolesLoading(true) getResourceRoles() .then(roles => { - setResourceRoles(roles) + setResourceRoles(_.orderBy(roles, ['name'])) setResourceRolesLoading(false) }) .catch(e => { diff --git a/src/apps/admin/src/lib/hooks/index.ts b/src/apps/admin/src/lib/hooks/index.ts index 809a1b375..c96c3ac3a 100644 --- a/src/apps/admin/src/lib/hooks/index.ts +++ b/src/apps/admin/src/lib/hooks/index.ts @@ -22,8 +22,6 @@ export * from './useManagePermissionGroups' export * from './useLoadUser' export * from './useLoadGroup' export * from './useManagePermissionGroupMembers' -export * from './useManageChallengeResources' export * from './useSearchUserInfo' -export * from './useManageAddChallengeResource' export * from './useManageBusEvent' export * from './useManageChallengeSubmissions' diff --git a/src/apps/admin/src/lib/hooks/useManageAddChallengeResource.ts b/src/apps/admin/src/lib/hooks/useManageAddChallengeResource.ts deleted file mode 100644 index 4c88952b8..000000000 --- a/src/apps/admin/src/lib/hooks/useManageAddChallengeResource.ts +++ /dev/null @@ -1,118 +0,0 @@ -/** - * Manage Add Challenge Resource - */ -import { useCallback, useReducer } from 'react' -import { toast } from 'react-toastify' - -import { FormAddResource, UserInfo } from '../models' -import { addChallengeResource } from '../services' -import { handleError } from '../utils' - -/// ///////////////// -// Add challenge resource reducer -/// //////////////// - -type AddChallengeResourceState = { - isAdding: boolean - userInfo?: UserInfo -} - -const AddChallengeResourceActionType = { - ADD_CHALLENGE_RESOURCE_DONE: 'ADD_CHALLENGE_RESOURCE_DONE' as const, - ADD_CHALLENGE_RESOURCE_FAILED: 'ADD_CHALLENGE_RESOURCE_FAILED' as const, - ADD_CHALLENGE_RESOURCE_INIT: 'ADD_CHALLENGE_RESOURCE_INIT' as const, -} - -type AddChallengeResourceReducerAction = { - type: - | typeof AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_DONE - | typeof AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_INIT - | typeof AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_FAILED -} - -const reducer = ( - previousState: AddChallengeResourceState, - action: AddChallengeResourceReducerAction, -): AddChallengeResourceState => { - switch (action.type) { - case AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_INIT: { - return { - ...previousState, - isAdding: true, - } - } - - case AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_DONE: { - return { - ...previousState, - isAdding: false, - } - } - - case AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_FAILED: { - return { - ...previousState, - isAdding: false, - } - } - - default: { - return previousState - } - } -} - -export interface useManageAddChallengeResourceProps { - isAdding: boolean - doAddChallengeResource: ( - data: FormAddResource, - callBack: () => void, - ) => void -} - -/** - * Manage add challenge resource redux state - * @param challengeId challenge id - * @returns state data - */ -export function useManageAddChallengeResource( - challengeId: string, -): useManageAddChallengeResourceProps { - const [state, dispatch] = useReducer(reducer, { - isAdding: false, - }) - - const doAddChallengeResource = useCallback( - (data: FormAddResource, callBack: () => void) => { - dispatch({ - type: AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_INIT, - }) - addChallengeResource({ - challengeId, - memberHandle: data.handle.label as string, - roleId: data.resourceRole.value as string, - }) - .then(() => { - toast.success('Challenge resource added successfully', { - toastId: 'Add challenge resource', - }) - dispatch({ - type: AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_DONE, - }) - callBack() - }) - .catch(e => { - dispatch({ - type: AddChallengeResourceActionType.ADD_CHALLENGE_RESOURCE_FAILED, - }) - handleError(e) - }) - }, - [dispatch, challengeId], - ) - - return { - doAddChallengeResource, - isAdding: state.isAdding, - } -} diff --git a/src/apps/admin/src/lib/hooks/useManageChallengeResources.ts b/src/apps/admin/src/lib/hooks/useManageChallengeResources.ts deleted file mode 100644 index 026f5e80f..000000000 --- a/src/apps/admin/src/lib/hooks/useManageChallengeResources.ts +++ /dev/null @@ -1,269 +0,0 @@ -/** - * Manage Challenge Resources - */ -import { - Dispatch, - SetStateAction, - useCallback, - useMemo, - useReducer, -} from 'react' -import { toast } from 'react-toastify' -import _ from 'lodash' - -import { Sort } from '~/apps/gamification-admin/src/game-lib' - -import { TABLE_PAGINATION_ITEM_PER_PAGE } from '../../config/index.config' -import { - adjustChallengeResource, - ChallengeResource, - IsRemovingType, -} from '../models' -import { deleteChallengeResource, getChallengeResources } from '../services' -import { handleError } from '../utils' - -import { - useTableFilterBackend, - useTableFilterBackendProps, -} from './useTableFilterBackend' - -/// ///////////////// -// Permission resources reducer -/// //////////////// - -type ResourceState = { - isLoading: boolean - resources: ChallengeResource[] - totalPages: number - isRemoving: IsRemovingType -} - -const ResourceActionType = { - FETCH_RESOURCES_DONE: 'FETCH_RESOURCES_DONE' as const, - FETCH_RESOURCES_FAILED: 'FETCH_RESOURCES_FAILED' as const, - FETCH_RESOURCES_INIT: 'FETCH_RESOURCES_INIT' as const, - REMOVE_RESOURCES_DONE: 'REMOVE_RESOURCES_DONE' as const, - REMOVE_RESOURCES_FAILED: 'REMOVE_RESOURCES_FAILED' as const, - REMOVE_RESOURCES_INIT: 'REMOVE_RESOURCES_INIT' as const, -} - -type ResourceReducerAction = - | { - type: - | typeof ResourceActionType.FETCH_RESOURCES_INIT - | typeof ResourceActionType.FETCH_RESOURCES_FAILED - } - | { - type: typeof ResourceActionType.FETCH_RESOURCES_DONE - payload: { - data: ChallengeResource[] - totalPages: number - } - } - | { - type: - | typeof ResourceActionType.REMOVE_RESOURCES_DONE - | typeof ResourceActionType.REMOVE_RESOURCES_INIT - | typeof ResourceActionType.REMOVE_RESOURCES_FAILED - payload: number - } - -const reducer = ( - previousState: ResourceState, - action: ResourceReducerAction, -): ResourceState => { - switch (action.type) { - case ResourceActionType.FETCH_RESOURCES_INIT: { - return { - ...previousState, - isLoading: true, - resources: [], - } - } - - case ResourceActionType.FETCH_RESOURCES_DONE: { - const payload = action.payload - return { - ...previousState, - isLoading: false, - resources: payload.data, - totalPages: payload.totalPages, - } - } - - case ResourceActionType.FETCH_RESOURCES_FAILED: { - return { - ...previousState, - isLoading: false, - } - } - - case ResourceActionType.REMOVE_RESOURCES_INIT: { - return { - ...previousState, - isRemoving: { - ...previousState.isRemoving, - [action.payload]: true, - }, - } - } - - case ResourceActionType.REMOVE_RESOURCES_DONE: { - return { - ...previousState, - isRemoving: { - ...previousState.isRemoving, - [action.payload]: false, - }, - } - } - - case ResourceActionType.REMOVE_RESOURCES_FAILED: { - return { - ...previousState, - isRemoving: { - ...previousState.isRemoving, - [action.payload]: false, - }, - } - } - - default: { - return previousState - } - } -} - -export interface useManageChallengeResourcesProps { - isLoading: boolean - resources: ChallengeResource[] - totalPages: number - page: number - setPage: Dispatch> - sort: Sort | undefined - setSort: Dispatch> - isRemoving: IsRemovingType - isRemovingBool: boolean - doRemoveResource: (data: ChallengeResource) => void -} - -/** - * Manage permission resources redux state - * @param challengeId challenge id - * @param mappingSortField mapping from property field to sort field - * @returns state data - */ -export function useManageChallengeResources( - challengeId: string, - mappingSortField?: { - [key: string]: string - }, -): useManageChallengeResourcesProps { - const [state, dispatch] = useReducer(reducer, { - isLoading: false, - isRemoving: {}, - resources: [], - totalPages: 1, - }) - const isRemovingBool = useMemo( - () => _.some(state.isRemoving, value => value === true), - [state.isRemoving], - ) - const { page, setPage, sort, setSort, setFilterCriteria }: useTableFilterBackendProps<{}> - = useTableFilterBackend<{}>( - (pageRequest, sortRequest, filterCriteria, success, fail) => { - if (challengeId) { - dispatch({ - type: ResourceActionType.FETCH_RESOURCES_INIT, - }) - let sortFieldName = sortRequest?.fieldName - if ( - mappingSortField - && sortFieldName - && mappingSortField[sortFieldName] - ) { - sortFieldName = mappingSortField[sortFieldName] - } - - getChallengeResources(challengeId, { - page: pageRequest, - perPage: TABLE_PAGINATION_ITEM_PER_PAGE, - ...sortRequest ? { - sortBy: sortFieldName, - sortOrder: sortRequest.direction, - } : {}, - }) - .then(result => { - dispatch({ - payload: { - data: result.data.map( - adjustChallengeResource, - ), - totalPages: result.totalPages, - }, - type: ResourceActionType.FETCH_RESOURCES_DONE, - }) - success() - }) - .catch(e => { - dispatch({ - type: ResourceActionType.FETCH_RESOURCES_FAILED, - }) - handleError(e) - fail() - }) - } else { - fail() - } - }, - {}, - ) - - const doRemoveResource = useCallback( - (item: ChallengeResource) => { - dispatch({ - payload: item.id, - type: ResourceActionType.REMOVE_RESOURCES_INIT, - }) - function handleActionError(error: any): void { - dispatch({ - payload: item.id, - type: ResourceActionType.REMOVE_RESOURCES_FAILED, - }) - handleError(error) - } - - deleteChallengeResource({ - challengeId, - memberHandle: item.memberHandle, - roleId: item.roleId, - }) - .then(() => { - toast.success('Resource removed successfully', { - toastId: 'Remove resource', - }) - dispatch({ - payload: item.id, - type: ResourceActionType.REMOVE_RESOURCES_DONE, - }) - setFilterCriteria({}) // fetch table data again - }) - .catch(handleActionError) - }, - // eslint-disable-next-line react-hooks/exhaustive-deps - [dispatch, challengeId], - ) - - return { - doRemoveResource, - isLoading: state.isLoading, - isRemoving: state.isRemoving, - isRemovingBool, - page, - resources: state.resources, - setPage, - setSort, - sort, - totalPages: state.totalPages, - } -} diff --git a/src/apps/admin/src/lib/hooks/useManagePermissionGroups.ts b/src/apps/admin/src/lib/hooks/useManagePermissionGroups.ts index 702cbc734..9f71ea3c1 100644 --- a/src/apps/admin/src/lib/hooks/useManagePermissionGroups.ts +++ b/src/apps/admin/src/lib/hooks/useManagePermissionGroups.ts @@ -23,7 +23,7 @@ type GroupsState = { // used to get all groups const PAGE = 1 -const PER_PAGE = 4000 +const PER_PAGE = 10000 const GroupsActionType = { ADD_GROUPS_DONE: 'ADD_GROUPS_DONE' as const, @@ -37,7 +37,6 @@ const GroupsActionType = { type GroupsReducerAction = | { type: - | typeof GroupsActionType.ADD_GROUPS_DONE | typeof GroupsActionType.ADD_GROUPS_INIT | typeof GroupsActionType.ADD_GROUPS_FAILED | typeof GroupsActionType.FETCH_GROUPS_INIT @@ -47,6 +46,10 @@ type GroupsReducerAction = type: typeof GroupsActionType.FETCH_GROUPS_DONE payload: UserGroup[] } + | { + type: typeof GroupsActionType.ADD_GROUPS_DONE + payload: UserGroup + } const reducer = ( previousState: GroupsState, @@ -87,6 +90,7 @@ const reducer = ( case GroupsActionType.ADD_GROUPS_DONE: { return { ...previousState, + groups: [action.payload, ...previousState.groups], isAdding: false, } } @@ -169,23 +173,32 @@ export function useManagePermissionGroups( dispatch({ type: GroupsActionType.ADD_GROUPS_INIT, }) - function handleSuccess(): void { + function handleSuccess(group: UserGroup): void { + if (group.createdBy && !group.createdByHandle) { + loadUser(group.createdBy) + } else if (!group.createdByHandle) { + group.createdByHandle = '' + } + + if (group.updatedBy && !group.updatedByHandle) { + loadUser(group.updatedBy) + } else if (!group.updatedByHandle) { + group.updatedByHandle = '' + } + toast.success('Group added successfully', { toastId: 'Add group', }) dispatch({ + payload: group, type: GroupsActionType.ADD_GROUPS_DONE, }) success() } createGroup(groupInfo) - .then(() => { - setTimeout(() => { - handleSuccess() - doFetchGroups() - }, 1000) // sometimes the backend does not return the new data - // so I added a 1 second timeout for this + .then((group: UserGroup) => { + handleSuccess(group) }) .catch(e => { dispatch({ @@ -194,7 +207,7 @@ export function useManagePermissionGroups( handleError(e) }) }, - [dispatch, doFetchGroups], + [dispatch, loadUser], ) useOnComponentDidMount(() => { diff --git a/src/apps/admin/src/lib/hooks/useManageUserGroups.ts b/src/apps/admin/src/lib/hooks/useManageUserGroups.ts index eb9f79f45..8967c36b3 100644 --- a/src/apps/admin/src/lib/hooks/useManageUserGroups.ts +++ b/src/apps/admin/src/lib/hooks/useManageUserGroups.ts @@ -23,7 +23,7 @@ import { useOnComponentDidMount } from './useOnComponentDidMount' // used to get all groups const PAGE = 1 -const PER_PAGE = 1000 +const PER_PAGE = 10000 type UserGroupsState = { isLoading: boolean diff --git a/src/apps/admin/src/lib/models/FormAddResource.model.ts b/src/apps/admin/src/lib/models/FormAddResource.model.ts deleted file mode 100644 index e9fdac817..000000000 --- a/src/apps/admin/src/lib/models/FormAddResource.model.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { SelectOption } from './SelectOption.model' - -/** - * Model for add resource form - */ -export interface FormAddResource { - userId: string - handle: SelectOption - resourceRole: SelectOption -} diff --git a/src/apps/admin/src/lib/models/Submission.model.ts b/src/apps/admin/src/lib/models/Submission.model.ts index 2b8a9ec77..872777ea5 100644 --- a/src/apps/admin/src/lib/models/Submission.model.ts +++ b/src/apps/admin/src/lib/models/Submission.model.ts @@ -53,7 +53,12 @@ export interface Submission { export function recalculateSubmissionRank( memberSubmissions: MemberSubmission[], ): MemberSubmission[] { - _.each(memberSubmissions, memberSubmission => { + const validMemberSubmissions: MemberSubmission[] = _.filter( + memberSubmissions, + memberSubmission => !!memberSubmission.submissions + && !!memberSubmission.submissions.length, + ) + _.each(validMemberSubmissions, memberSubmission => { memberSubmission.submissions = memberSubmission.submissions.map(adjustSubmissionResponse) }) @@ -61,7 +66,7 @@ export function recalculateSubmissionRank( maxFinalScore: number; submissions: MemberSubmission[]; } - = processRanks(memberSubmissions) + = processRanks(validMemberSubmissions) finalSubmissions.sort((a, b) => { if (maxFinalScore === 0) { return (a.provisionalRank ?? 0) - (b.provisionalRank ?? 0) diff --git a/src/apps/admin/src/lib/models/index.ts b/src/apps/admin/src/lib/models/index.ts index 1e7960c6a..786d0dc93 100644 --- a/src/apps/admin/src/lib/models/index.ts +++ b/src/apps/admin/src/lib/models/index.ts @@ -30,7 +30,6 @@ export * from './SearchUserInfo.model' export * from './FormAddGroup.model' export * from './FormGroupMembersFilters.model' export * from './RoleMemberInfo.model' -export * from './FormAddResource.model' export * from './Submission.model' export * from './RequestBusAPI.model' export * from './MemberSubmission.model' diff --git a/src/apps/admin/src/lib/services/groups.service.ts b/src/apps/admin/src/lib/services/groups.service.ts index b1f259dc1..f2b146ec5 100644 --- a/src/apps/admin/src/lib/services/groups.service.ts +++ b/src/apps/admin/src/lib/services/groups.service.ts @@ -1,6 +1,7 @@ /** * Groups service */ +import _ from 'lodash' import qs from 'qs' import { EnvironmentConfig } from '~/config' @@ -80,7 +81,7 @@ export const fetchGroups = async (params: { const result = await xhrGetAsync( `${EnvironmentConfig.API.V5}/groups?${qs.stringify(params)}`, ) - return result.map(adjustUserGroupResponse) + return _.orderBy(result.map(adjustUserGroupResponse), ['createdAt'], ['desc']) } /** @@ -94,6 +95,15 @@ export const createGroup = async (data: FormAddGroup): Promise => { `${EnvironmentConfig.API.V5}/groups`, data, ) + + if (!result.updatedAt) { + result.updatedAt = result.createdAt + } + + if (!result.updatedBy) { + result.updatedBy = '00000000' + result.updatedByHandle = '00000000 (not found)' + } return adjustUserGroupResponse(result) } diff --git a/src/apps/admin/src/lib/utils/validation.ts b/src/apps/admin/src/lib/utils/validation.ts index 39c370f0b..89e34212a 100644 --- a/src/apps/admin/src/lib/utils/validation.ts +++ b/src/apps/admin/src/lib/utils/validation.ts @@ -4,7 +4,6 @@ import _ from 'lodash' import { FormAddGroup, FormAddGroupMembers, - FormAddResource, FormBillingAccountsFilter, FormClientsFilter, FormEditBillingAccount, @@ -38,33 +37,6 @@ export const formUsersFiltersSchema: Yup.ObjectSchema .optional(), }) -/** - * validation schema for form add resource - */ -export const formAddResourceSchema: Yup.ObjectSchema - = Yup.object({ - handle: Yup.object() - .shape({ - label: Yup.string() - .required('Label is required.'), - value: Yup.number() - .required('Value is required.'), - }) - .default(undefined) - .required('Handle is required.'), - resourceRole: Yup.object() - .shape({ - label: Yup.string() - .required('Label is required.'), - value: Yup.string() - .required('Value is required.'), - }) - .default(undefined) - .required('Role is required.'), - userId: Yup.string() - .required('User id is required.'), - }) - /** * validation schema for form billing accounts filter */ From 58b54908c89b94798c8e787b40298235ab31cbb8 Mon Sep 17 00:00:00 2001 From: dat Date: Fri, 13 Jun 2025 13:27:10 +0700 Subject: [PATCH 2/2] fix lint --- src/apps/admin/src/lib/services/groups.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/apps/admin/src/lib/services/groups.service.ts b/src/apps/admin/src/lib/services/groups.service.ts index f2b146ec5..9c1a51a6f 100644 --- a/src/apps/admin/src/lib/services/groups.service.ts +++ b/src/apps/admin/src/lib/services/groups.service.ts @@ -104,6 +104,7 @@ export const createGroup = async (data: FormAddGroup): Promise => { result.updatedBy = '00000000' result.updatedByHandle = '00000000 (not found)' } + return adjustUserGroupResponse(result) }