mirror of https://gitee.com/answerdev/answer.git
Merge branch 'ui-v0.3' of git.backyard.segmentfault.com:opensource/answer into ui-v0.3
This commit is contained in:
commit
787193cf55
|
@ -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 {
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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],
|
||||||
|
|
Loading…
Reference in New Issue