feat: Add badges to admin

This commit is contained in:
robin
2024-08-05 14:29:22 +08:00
parent ecbfd61bac
commit d055507c80
7 changed files with 198 additions and 1 deletions
+15 -1
View File
@@ -1692,6 +1692,7 @@ ui:
questions: Questions
answers: Answers
users: Users
badges: Badges
flags: Flags
settings: Settings
general: General
@@ -2117,7 +2118,20 @@ ui:
msg:
should_be_number: the input should be number
number_larger_1: number should be equal or larger than 1
badges:
action: Action
active: Active
all: All
awards: Awards
deactivate: Deactivate
filter:
placeholder: Filter by name, badge:id
group: Group
inactive: Inactive
name: Name
show_logs: Show logs
status: Status
title: Badges
form:
optional: (optional)
empty: cannot be empty
+3
View File
@@ -93,6 +93,9 @@ export const ADMIN_NAV_MENUS = [
{
name: 'users',
},
{
name: 'badges',
},
{
name: 'customize',
children: [
+2
View File
@@ -328,6 +328,8 @@ export type UserFilterBy =
| 'suspended'
| 'deleted';
export type BadgeFilterBy = 'all' | 'active' | 'inactive';
export type InstalledPluginsFilterBy =
| 'all'
| 'active'
@@ -0,0 +1,51 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { Dropdown } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { Icon } from '@/components';
interface Props {
badgeData;
}
const UserOperation = ({ badgeData }: Props) => {
const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' });
console.log(badgeData);
return (
<td className="text-end">
<Dropdown>
<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.Divider />
<Dropdown.Item>{t('show_logs')}</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</td>
);
};
export default UserOperation;
+122
View File
@@ -0,0 +1,122 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { FC } from 'react';
import { Form, Table, Stack } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { QueryGroup } from '@/components';
import * as Type from '@/common/interface';
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 Users: FC = () => {
const { t } = useTranslation('translation', { keyPrefix: 'admin.badges' });
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const curFilter = urlSearchParams.get('filter') || BadgeFilterKeys[0];
const curQuery = urlSearchParams.get('query') || '';
const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page');
setUrlSearchParams(urlSearchParams);
};
return (
<>
<h3 className="mb-4">{t('title')}</h3>
<div className="d-flex flex-wrap justify-content-between align-items-center mb-3">
<Stack direction="horizontal" gap={3}>
<QueryGroup
data={BadgeFilterKeys}
currentSort={curFilter}
sortKey="filter"
i18nKeyPrefix="admin.badges"
/>
</Stack>
<Form.Control
size="sm"
type="search"
value={curQuery}
onChange={handleFilter}
placeholder={t('filter.placeholder')}
style={{ width: '12.25rem' }}
className="mt-3 mt-sm-0"
/>
</div>
<Table responsive="md">
<thead>
<tr>
<th>{t('name')}</th>
<th>{t('group')}</th>
<th>{t('awards')}</th>
<th>{t('status')}</th>
<th className="text-end">{t('action')}</th>
</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-bc49RNcO0CkHifof2hn4oLyaC4fUd6GBrthOpXg__"
className="rounded-circle bg-white me-2"
width="32px"
height="32px"
alt="badge"
/>
<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>
</tbody>
</Table>
{/* {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> */}
</>
);
};
export default Users;
+1
View File
@@ -37,6 +37,7 @@ const g10Paths = [
'questions',
'answers',
'users',
'badges',
'flags',
'installed-plugins',
];
+4
View File
@@ -400,6 +400,10 @@ const routes: RouteNode[] = [
path: ':slug_name',
page: 'pages/Admin/Plugins/Config',
},
{
path: 'badges',
page: 'pages/Admin/Badges',
},
],
},
{