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

This commit is contained in:
haitao(lj) 2022-11-02 17:14:56 +08:00
commit 787193cf55
6 changed files with 90 additions and 27 deletions

View File

@ -228,6 +228,7 @@ export type AdminContentsFilterBy = 'normal' | 'closed' | 'deleted';
export interface AdminContentsReq extends Paging { export interface AdminContentsReq extends Paging {
status: AdminContentsFilterBy; status: AdminContentsFilterBy;
query?: string;
} }
/** /**
@ -263,6 +264,7 @@ export interface AdminSettingsInterface {
logo: string; logo: string;
language: string; language: string;
theme: string; theme: string;
time_zone: string;
} }
export interface AdminSettingsSmtp { export interface AdminSettingsSmtp {

View File

@ -759,7 +759,30 @@
"dashboard": { "dashboard": {
"title": "Dashboard", "title": "Dashboard",
"welcome": "Welcome to Answer Admin !", "welcome": "Welcome to Answer Admin !",
"version": "Version" "site_statistics": "Site Statistics",
"questions": "Questions:",
"answers": "Answers:",
"comments": "Comments:",
"votes": "Votes:",
"active_users": "Active users:",
"flags": "Flags:",
"site_health_status": "Site Health Status",
"version": "Version:",
"https": "HTTPS:",
"uploading_files": "Uploading files:",
"smtp": "SMTP:",
"timezone": "Timezone:",
"system_info": "System Info",
"storage_used": "Storage used:",
"uptime": "Uptime:",
"answer_links": "Answer Links",
"documents": "Documents",
"feedback": "Feedback",
"review": "Review",
"config": "Config",
"update_to": "Update to",
"latest": "Latest",
"check_failed": "Check failed"
}, },
"flags": { "flags": {
"title": "Flags", "title": "Flags",
@ -816,7 +839,10 @@
"inactive": "Inactive", "inactive": "Inactive",
"suspended": "Suspended", "suspended": "Suspended",
"deleted": "Deleted", "deleted": "Deleted",
"normal": "Normal" "normal": "Normal",
"filter": {
"placeholder": "Filter by name, user:id"
}
}, },
"questions": { "questions": {
"page_title": "Questions", "page_title": "Questions",
@ -829,7 +855,10 @@
"created": "Created", "created": "Created",
"status": "Status", "status": "Status",
"action": "Action", "action": "Action",
"change": "Change" "change": "Change",
"filter": {
"placeholder": "Filter by title, question:id"
}
}, },
"answers": { "answers": {
"page_title": "Answers", "page_title": "Answers",
@ -840,7 +869,10 @@
"created": "Created", "created": "Created",
"status": "Status", "status": "Status",
"action": "Action", "action": "Action",
"change": "Change" "change": "Change",
"filter": {
"placeholder": "Filter by title, answer:id"
}
}, },
"general": { "general": {
"page_title": "General", "page_title": "General",
@ -876,6 +908,11 @@
"label": "Interface Language", "label": "Interface Language",
"msg": "Interface language cannot be empty.", "msg": "Interface language cannot be empty.",
"text": "User interface language. It will change when you refresh the page." "text": "User interface language. It will change when you refresh the page."
},
"timezone": {
"label": "Timezone",
"msg": "Timezone cannot be empty.",
"text": "Choose a UTC (Coordinated Universal Time) time offset."
} }
}, },
"smtp": { "smtp": {

View File

@ -22,10 +22,12 @@ import '../index.scss';
const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted']; const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted'];
const Answers: FC = () => { const Answers: FC = () => {
const [urlSearchParams] = useSearchParams(); const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const curFilter = urlSearchParams.get('status') || answerFilterItems[0]; const curFilter = urlSearchParams.get('status') || answerFilterItems[0];
const PAGE_SIZE = 20; const PAGE_SIZE = 20;
const curPage = Number(urlSearchParams.get('page')) || 1; const curPage = Number(urlSearchParams.get('page')) || 1;
const curQuery = urlSearchParams.get('query') || '';
const questionId = urlSearchParams.get('questionId') || '';
const { t } = useTranslation('translation', { keyPrefix: 'admin.answers' }); const { t } = useTranslation('translation', { keyPrefix: 'admin.answers' });
const { const {
@ -36,6 +38,8 @@ const Answers: FC = () => {
page_size: PAGE_SIZE, page_size: PAGE_SIZE,
page: curPage, page: curPage,
status: curFilter as Type.AdminContentsFilterBy, status: curFilter as Type.AdminContentsFilterBy,
query: curQuery,
question_id: questionId,
}); });
const count = listData?.count || 0; const count = listData?.count || 0;
@ -77,6 +81,11 @@ const Answers: FC = () => {
}); });
}; };
const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page');
setUrlSearchParams(urlSearchParams);
};
return ( return (
<> <>
<h3 className="mb-4">{t('page_title')}</h3> <h3 className="mb-4">{t('page_title')}</h3>
@ -89,19 +98,20 @@ const Answers: FC = () => {
/> />
<Form.Control <Form.Control
value={curQuery}
onChange={handleFilter}
size="sm" size="sm"
type="input" type="input"
placeholder="Filter by title" placeholder={t('filter.placeholder')}
className="d-none"
style={{ width: '12.25rem' }} style={{ width: '12.25rem' }}
/> />
</div> </div>
<Table> <Table responsive>
<thead> <thead>
<tr> <tr>
<th style={{ width: '45%' }}>{t('post')}</th> <th>{t('post')}</th>
<th>{t('votes')}</th> <th>{t('votes')}</th>
<th style={{ width: '20%' }}>{t('created')}</th> <th>{t('created')}</th>
<th>{t('status')}</th> <th>{t('status')}</th>
{curFilter !== 'deleted' && <th>{t('action')}</th>} {curFilter !== 'deleted' && <th>{t('action')}</th>}
</tr> </tr>
@ -132,6 +142,7 @@ const Answers: FC = () => {
__html: li.description, __html: li.description,
}} }}
className="last-p text-truncate-2 fs-14" className="last-p text-truncate-2 fs-14"
style={{ maxWidth: '30rem' }}
/> />
</Stack> </Stack>
</td> </td>

View File

@ -1,6 +1,6 @@
import { FC } from 'react'; import { FC } from 'react';
import { Button, Form, Table, Stack, Badge } from 'react-bootstrap'; import { Button, Form, Table, Stack, Badge } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom'; import { Link, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { import {
@ -31,9 +31,10 @@ const questionFilterItems: Type.AdminContentsFilterBy[] = [
const PAGE_SIZE = 20; const PAGE_SIZE = 20;
const Questions: FC = () => { const Questions: FC = () => {
const [urlSearchParams] = useSearchParams(); const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const curFilter = urlSearchParams.get('status') || questionFilterItems[0]; const curFilter = urlSearchParams.get('status') || questionFilterItems[0];
const curPage = Number(urlSearchParams.get('page')) || 1; const curPage = Number(urlSearchParams.get('page')) || 1;
const curQuery = urlSearchParams.get('query') || '';
const { t } = useTranslation('translation', { keyPrefix: 'admin.questions' }); const { t } = useTranslation('translation', { keyPrefix: 'admin.questions' });
const { const {
@ -44,6 +45,7 @@ const Questions: FC = () => {
page_size: PAGE_SIZE, page_size: PAGE_SIZE,
page: curPage, page: curPage,
status: curFilter as Type.AdminContentsFilterBy, status: curFilter as Type.AdminContentsFilterBy,
query: curQuery,
}); });
const count = listData?.count || 0; const count = listData?.count || 0;
@ -96,6 +98,11 @@ const Questions: FC = () => {
}); });
}; };
const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page');
setUrlSearchParams(urlSearchParams);
};
return ( return (
<> <>
<h3 className="mb-4">{t('page_title')}</h3> <h3 className="mb-4">{t('page_title')}</h3>
@ -108,10 +115,11 @@ const Questions: FC = () => {
/> />
<Form.Control <Form.Control
value={curQuery}
size="sm" size="sm"
type="input" type="input"
placeholder="Filter by title" placeholder={t('filter.placeholder')}
className="d-none" onChange={handleFilter}
style={{ width: '12.25rem' }} style={{ width: '12.25rem' }}
/> />
</div> </div>
@ -147,12 +155,11 @@ const Questions: FC = () => {
</td> </td>
<td>{li.vote_count}</td> <td>{li.vote_count}</td>
<td> <td>
<a <Link
href={`/questions/${li.id}`} to={`/admin/answers?questionId=${li.id}`}
target="_blank"
rel="noreferrer"> rel="noreferrer">
{li.answer_count} {li.answer_count}
</a> </Link>
</td> </td>
<td> <td>
<Stack> <Stack>

View File

@ -1,4 +1,4 @@
import { FC, useState } from 'react'; import { FC } from 'react';
import { Button, Form, Table, Badge } from 'react-bootstrap'; import { Button, Form, Table, Badge } from 'react-bootstrap';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -33,11 +33,11 @@ const bgMap = {
const PAGE_SIZE = 10; const PAGE_SIZE = 10;
const Users: FC = () => { const Users: FC = () => {
const { t } = useTranslation('translation', { keyPrefix: 'admin.users' }); const { t } = useTranslation('translation', { keyPrefix: 'admin.users' });
const [userName, setUserName] = useState('');
const [urlSearchParams] = useSearchParams(); const [urlSearchParams, setUrlSearchParams] = useSearchParams();
const curFilter = urlSearchParams.get('filter') || UserFilterKeys[0]; const curFilter = urlSearchParams.get('filter') || UserFilterKeys[0];
const curPage = Number(urlSearchParams.get('page') || '1'); const curPage = Number(urlSearchParams.get('page') || '1');
const curQuery = urlSearchParams.get('query') || '';
const { const {
data, data,
isLoading, isLoading,
@ -45,7 +45,7 @@ const Users: FC = () => {
} = useQueryUsers({ } = useQueryUsers({
page: curPage, page: curPage,
page_size: PAGE_SIZE, page_size: PAGE_SIZE,
...(userName ? { username: userName } : {}), query: curQuery,
...(curFilter === 'all' ? {} : { status: curFilter }), ...(curFilter === 'all' ? {} : { status: curFilter }),
}); });
const changeModal = useChangeModal({ const changeModal = useChangeModal({
@ -59,6 +59,11 @@ const Users: FC = () => {
}); });
}; };
const handleFilter = (e) => {
urlSearchParams.set('query', e.target.value);
urlSearchParams.delete('page');
setUrlSearchParams(urlSearchParams);
};
return ( return (
<> <>
<h3 className="mb-4">{t('title')}</h3> <h3 className="mb-4">{t('title')}</h3>
@ -71,11 +76,10 @@ const Users: FC = () => {
/> />
<Form.Control <Form.Control
className="d-none"
size="sm" size="sm"
value={userName} value={curQuery}
onChange={(e) => setUserName(e.target.value)} onChange={handleFilter}
placeholder="Filter by name" placeholder={t('filter.placeholder')}
style={{ width: '12.25rem' }} style={{ width: '12.25rem' }}
/> />
</div> </div>

View File

@ -4,7 +4,9 @@ import qs from 'qs';
import request from '@/utils/request'; import request from '@/utils/request';
import type * as Type from '@/common/interface'; import type * as Type from '@/common/interface';
export const useAnswerSearch = (params: Type.AdminContentsReq) => { export const useAnswerSearch = (
params: Type.AdminContentsReq & { question_id?: string },
) => {
const apiUrl = `/answer/admin/api/answer/page?${qs.stringify(params)}`; const apiUrl = `/answer/admin/api/answer/page?${qs.stringify(params)}`;
const { data, error, mutate } = useSWR<Type.ListResult, Error>( const { data, error, mutate } = useSWR<Type.ListResult, Error>(
[apiUrl], [apiUrl],