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 {
status: AdminContentsFilterBy;
query?: string;
}
/**
@ -263,6 +264,7 @@ export interface AdminSettingsInterface {
logo: string;
language: string;
theme: string;
time_zone: string;
}
export interface AdminSettingsSmtp {

View File

@ -759,7 +759,30 @@
"dashboard": {
"title": "Dashboard",
"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": {
"title": "Flags",
@ -816,7 +839,10 @@
"inactive": "Inactive",
"suspended": "Suspended",
"deleted": "Deleted",
"normal": "Normal"
"normal": "Normal",
"filter": {
"placeholder": "Filter by name, user:id"
}
},
"questions": {
"page_title": "Questions",
@ -829,7 +855,10 @@
"created": "Created",
"status": "Status",
"action": "Action",
"change": "Change"
"change": "Change",
"filter": {
"placeholder": "Filter by title, question:id"
}
},
"answers": {
"page_title": "Answers",
@ -840,7 +869,10 @@
"created": "Created",
"status": "Status",
"action": "Action",
"change": "Change"
"change": "Change",
"filter": {
"placeholder": "Filter by title, answer:id"
}
},
"general": {
"page_title": "General",
@ -876,6 +908,11 @@
"label": "Interface Language",
"msg": "Interface language cannot be empty.",
"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": {

View File

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

View File

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

View File

@ -4,7 +4,9 @@ import qs from 'qs';
import request from '@/utils/request';
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 { data, error, mutate } = useSWR<Type.ListResult, Error>(
[apiUrl],