This is an automated email from the ASF dual-hosted git repository. robin0716 pushed a commit to branch feat/1.4.0/personal in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
commit a4c8256fbd82315173f8e1d247f5cce2adeeedf5 Author: robin <[email protected]> AuthorDate: Tue Aug 13 17:42:12 2024 +0800 feat(admin): Improve the badge list --- .../pages/Admin/Badges/components/Action/index.tsx | 15 ++-- ui/src/pages/Admin/Badges/index.tsx | 96 +++++++++++++++------- ui/src/services/admin/{index.ts => badges.ts} | 30 +++++-- ui/src/services/admin/index.ts | 1 + 4 files changed, 97 insertions(+), 45 deletions(-) diff --git a/ui/src/pages/Admin/Badges/components/Action/index.tsx b/ui/src/pages/Admin/Badges/components/Action/index.tsx index 77601cbb..9ec18379 100644 --- a/ui/src/pages/Admin/Badges/components/Action/index.tsx +++ b/ui/src/pages/Admin/Badges/components/Action/index.tsx @@ -23,23 +23,20 @@ import { useTranslation } from 'react-i18next'; import { Icon } from '@/components'; interface Props { - badgeData; + onSelect: (eventKey: string | null) => void; } - -const UserOperation = ({ badgeData }: Props) => { +const BadgeOperation = ({ onSelect }: Props) => { const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' }); - console.log(badgeData); - return ( <td className="text-end"> - <Dropdown> + <Dropdown onSelect={onSelect}> <Dropdown.Toggle variant="link" className="no-toggle p-0"> <Icon name="three-dots-vertical" title={t('action')} /> </Dropdown.Toggle> <Dropdown.Menu align="end"> - <Dropdown.Item>{t('active')}</Dropdown.Item> - <Dropdown.Item>{t('deactivate')}</Dropdown.Item> + <Dropdown.Item eventKey="active">{t('active')}</Dropdown.Item> + <Dropdown.Item eventKey="inactive">{t('deactivate')}</Dropdown.Item> <Dropdown.Divider /> <Dropdown.Item>{t('show_logs')}</Dropdown.Item> </Dropdown.Menu> @@ -48,4 +45,4 @@ const UserOperation = ({ badgeData }: Props) => { ); }; -export default UserOperation; +export default BadgeOperation; diff --git a/ui/src/pages/Admin/Badges/index.tsx b/ui/src/pages/Admin/Badges/index.tsx index 28854012..6c74f3ce 100644 --- a/ui/src/pages/Admin/Badges/index.tsx +++ b/ui/src/pages/Admin/Badges/index.tsx @@ -22,33 +22,50 @@ import { Form, Table, Stack } from 'react-bootstrap'; import { useSearchParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { QueryGroup } from '@/components'; +import classNames from 'classnames'; + +import { Empty, Icon, Pagination, QueryGroup } from '@/components'; import * as Type from '@/common/interface'; +import { useQueryBadges, updateBadgeStatus } from '@/services/admin/badges'; import Action from './components/Action'; const BadgeFilterKeys: Type.BadgeFilterBy[] = ['all', 'active', 'inactive']; -// const bgMap = { -// normal: 'text-bg-success', -// suspended: 'text-bg-danger', -// deleted: 'text-bg-danger', -// inactive: 'text-bg-secondary', -// }; +const bgMap = { + active: 'text-bg-success', + inactive: 'text-bg-secondary', +}; + +const PAGE_SIZE = 10; const Users: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' }); const [urlSearchParams, setUrlSearchParams] = useSearchParams(); + const curPage = Number(urlSearchParams.get('page') || '1'); const curFilter = urlSearchParams.get('filter') || BadgeFilterKeys[0]; const curQuery = urlSearchParams.get('query') || ''; + const { data, isLoading, mutate } = useQueryBadges({ + page: curPage, + pageSize: PAGE_SIZE, + query: curQuery, + ...(curFilter === 'all' ? {} : { status: curFilter }), + }); + const handleFilter = (e) => { urlSearchParams.set('query', e.target.value); urlSearchParams.delete('page'); setUrlSearchParams(urlSearchParams); }; + const handleBadgeStatus = (badgeId, status) => { + updateBadgeStatus({ id: badgeId, status }).then(() => { + mutate(); + }); + }; + return ( <> <h3 className="mb-4">{t('title')}</h3> @@ -85,36 +102,57 @@ const Users: FC = () => { </tr> </thead> <tbody className="align-middle"> - <tr> - <td className="d-flex align-items-center"> - <img - src="https://s3-alpha-sig.figma.com/img/b6d9/2c6b/dfa4017fd4654f72c13bfc406377416a?Expires=1723420800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=lp0b2MuP5yiv7IB4yEuhFk--W9D5CWsud77ftgjtdFrSIPPsIxcnZxz-RyLl40euIysaQLVVWwvYqJP75wLnccCQ1XbzwKfU1sOj3Z52jMTLMZ5PGwYL~dnx0sUJVv3khew7Xe8FiebLTwK4yV62jlW2RYq~HvK3s3RL5z9ZrkSnZUIWOC1nD~RTlsS9K3-hJ9GHwSCA9i0VupM5qHBMgxZDstTy6MO5VnACCCD1865NsKpkLM770wlVXP7XARVl5AhcRFYr0J8VTjccwg3dRHvOUUy4sM0wOqRctX7dgQfp1V-bc49RNcO0CkHifof2hn4oLyaC4fU [...] - className="rounded-circle bg-white me-2" - width="32px" - height="32px" - alt="badge" + {data?.list.map((badge) => ( + <tr> + <td className="d-flex align-items-center"> + {badge.icon?.startsWith('http') ? ( + <img + src={badge.icon} + width={32} + height={32} + alt={badge.name} + className="me-3" + /> + ) : ( + <Icon + name={badge?.icon} + size="32px" + className={classNames( + 'lh-1 me-3', + badge?.level === 1 && 'bronze', + badge?.level === 2 && 'silver', + badge?.level === 3 && 'gold', + )} + /> + )} + <div> + <div className="text-primary">{badge.name}</div> + <div className="text-small">{badge.description}</div> + </div> + </td> + + <td>{badge.group_name}</td> + <td className="text-primary">{badge.award_count}</td> + <td> + <span className={classNames('badge', bgMap[badge.status])}> + {t(badge.status)} + </span> + </td> + <Action + onSelect={(status) => handleBadgeStatus(badge.id, status)} /> - <div> - <div className="text-primary">Nice Question</div> - <div className="text-small">Question score of 10 or more.</div> - </div> - </td> - - <td>Community Badges</td> - <td className="text-primary">200</td> - <td>Active</td> - <Action badgeData={{}} /> - </tr> + </tr> + ))} </tbody> </Table> - {/* {Number(data?.count) <= 0 && !isLoading && <Empty />} */} - {/* <div className="mt-4 mb-2 d-flex justify-content-center"> + {Number(data?.count) <= 0 && !isLoading && <Empty />} + <div className="mt-4 mb-2 d-flex justify-content-center"> <Pagination currentPage={curPage} totalSize={data?.count || 0} pageSize={PAGE_SIZE} /> - </div> */} + </div> </> ); }; diff --git a/ui/src/services/admin/index.ts b/ui/src/services/admin/badges.ts similarity index 59% copy from ui/src/services/admin/index.ts copy to ui/src/services/admin/badges.ts index ce64c511..b984164e 100644 --- a/ui/src/services/admin/index.ts +++ b/ui/src/services/admin/badges.ts @@ -17,10 +17,26 @@ * under the License. */ -export * from './answer'; -export * from './flag'; -export * from './question'; -export * from './settings'; -export * from './users'; -export * from './dashboard'; -export * from './plugins'; +import qs from 'qs'; +import useSWR from 'swr'; + +import request from '@/utils/request'; +import type * as Type from '@/common/interface'; + +export const useQueryBadges = (params) => { + const apiUrl = `/answer/admin/api/badges?${qs.stringify(params)}`; + const { data, error, mutate } = useSWR<Type.ListResult, Error>( + apiUrl, + request.instance.get, + ); + return { + data, + isLoading: !data && !error, + error, + mutate, + }; +}; + +export const updateBadgeStatus = (params) => { + return request.put('/answer/admin/api/badge/status', params); +}; diff --git a/ui/src/services/admin/index.ts b/ui/src/services/admin/index.ts index ce64c511..af83d365 100644 --- a/ui/src/services/admin/index.ts +++ b/ui/src/services/admin/index.ts @@ -24,3 +24,4 @@ export * from './settings'; export * from './users'; export * from './dashboard'; export * from './plugins'; +export * from './badges';
