Merge branch 'feat/ui-0.7.0' of git.backyard.segmentfault.com:opensource/answer into feat/ui-0.7.0

This commit is contained in:
haitao(lj) 2022-12-14 15:08:56 +08:00
commit 32f009b096
12 changed files with 130 additions and 8 deletions

View File

@ -248,6 +248,7 @@ ui:
install: Answer Installation
upgrade: Answer Upgrade
maintenance: Website Maintenance
users: Users
notifications:
title: Notifications
inbox: Inbox
@ -1104,7 +1105,7 @@ ui:
fields:
display_name:
label: Display Name
msg: display_name must be at maximum 30 characters in length.
msg: display_name must be at 4 - 30 characters in length.
email:
label: Email
msg: Email is not valid.
@ -1346,3 +1347,12 @@ ui:
by: By
comment: Comment
no_data: "We couldn't find anything."
users:
title: Users
users_with_the_most_reputation: Users with the highest reputation scores
users_with_the_most_vote: Users who voted the most
staffs: Our community staff
reputation: reputation
votes: votes

View File

@ -503,3 +503,11 @@ export interface MemberActionItem {
name: string;
type: string;
}
export interface User {
username: string;
rank: number;
vote_count: number;
display_name: string;
avatar: string;
}

View File

@ -12,6 +12,9 @@ import { marked } from 'marked';
import { htmlRender } from './utils';
let scrollTop = 0;
marked.setOptions({
breaks: true,
});
const Index = ({ value }, ref) => {
const [html, setHtml] = useState('');

View File

@ -46,7 +46,7 @@ const Index: FC = () => {
{t('save')}
</Button>
</Card.Header>
<Card.Body className="my-n1">
<Card.Body>
<TagSelector
value={followingTags}
onChange={handleTagsChange}
@ -67,14 +67,14 @@ const Index: FC = () => {
{t('edit')}
</Button>
</Card.Header>
<Card.Body className="m-n1">
<Card.Body>
{followingTags?.length ? (
<>
<div className="m-n1">
{followingTags.map((item) => {
const slugName = item?.slug_name;
return <Tag key={slugName} className="m-1" data={item} />;
})}
</>
</div>
) : (
<>
<div className="text-muted">{t('follow_tag_tip')}</div>

View File

@ -144,6 +144,9 @@ const Header: FC = () => {
<NavLink className="nav-link" to="/tags">
{t('header.nav.tag')}
</NavLink>
<NavLink className="nav-link" to="/users">
{t('header.nav.user')}
</NavLink>
</Nav>
</Col>
<hr className="hr lg-none mt-2" />

View File

@ -181,7 +181,7 @@ const TagSelector: FC<IProps> = ({
onFocus={onFocus}
onBlur={onBlur}
onKeyDown={handleKeyDown}>
<div className="d-flex flex-wrap mx-n1">
<div className="d-flex flex-wrap m-n1">
{initialValue?.map((item, index) => {
return (
<Button

View File

@ -45,7 +45,10 @@ const useAddUserModal = (props: IProps = {}) => {
display_name: {
'ui:options': {
validator: (value) => {
if (value.length > 30) {
const MIN_LENGTH = 4;
const MAX_LENGTH = 30;
if (value.length < MIN_LENGTH || value.length > MAX_LENGTH) {
return t('form.fields.display_name.msg');
}
return true;

View File

@ -413,7 +413,7 @@ const Index: React.FC = () => {
<Form.Label>{t('website.label')}</Form.Label>
<Form.Control
required
type="text"
type="url"
placeholder={t('website.placeholder')}
value={formData.website.value}
isInvalid={formData.website.isInvalid}

View File

@ -0,0 +1,77 @@
import { Container, Row, Col } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { usePageTags } from '@/hooks';
import { useQueryContributeUsers } from '@/services';
import { Avatar } from '@/components';
const Users = () => {
const { t } = useTranslation('translation', { keyPrefix: 'users' });
const { data: users } = useQueryContributeUsers();
usePageTags({
title: t('users', { keyPrefix: 'page_title' }),
});
if (!users) {
return null;
}
const keys = Object.keys(users);
return (
<Container className="py-3 my-3">
<Row className="mb-4 d-flex justify-content-center">
<Col xxl={10} sm={12}>
<h3 className="mb-4">{t('title')}</h3>
</Col>
<Col xxl={10} sm={12}>
{keys.map((key, index) => {
if (users[key]?.length === 0) {
return null;
}
return (
<>
<Row className="mb-4">
<Col>
<h6 className="mb-0">{t(key)}</h6>
</Col>
</Row>
<Row className={index === keys.length - 1 ? '' : 'mb-4'}>
{users[key]?.map((user) => (
<Col
key={user.username}
xs={12}
lg={3}
md={4}
sm={6}
className="mb-4">
<div className="d-flex">
<Avatar size="48px" avatar={user?.avatar} />
<div className="ms-2">
<Link to={`/users/${user.username}`}>
{user.display_name}
</Link>
<div className="text-secondary fs-14">
{key === 'users_with_the_most_vote'
? `${user.vote_count} ${t('votes')}`
: `${user.rank} ${t('reputation')}`}
</div>
</div>
</div>
</Col>
))}
</Row>
</>
);
})}
</Col>
</Row>
</Container>
);
};
export default Users;

View File

@ -92,6 +92,10 @@ const routes: RouteNode[] = [
},
},
// for users
{
path: 'users',
page: 'pages/Users',
},
{
path: 'users/:username',
page: 'pages/Users/Personal',

View File

@ -8,3 +8,4 @@ export * from './settings';
export * from './legal';
export * from './timeline';
export * from './revision';
export * from './user';

View File

@ -0,0 +1,13 @@
import useSWR from 'swr';
import request from '@/utils/request';
import type * as Type from '@/common/interface';
export const useQueryContributeUsers = () => {
const apiUrl = '/answer/api/v1/user/ranking';
return useSWR<{
users_with_the_most_reputation: Type.User[];
users_with_the_most_vote: Type.User[];
staffs: Type.User[];
}>(apiUrl, request.instance.get);
};