mirror of https://gitee.com/answerdev/answer.git
Merge branch 'ui' into 'main'
Ui See merge request opensource/answer!19
This commit is contained in:
commit
8287915659
|
@ -26,6 +26,7 @@ module.exports = {
|
|||
const config = configFunction(proxy, allowedHost);
|
||||
config.proxy = {
|
||||
'/answer': {
|
||||
// target: "http://10.0.20.84:8080",
|
||||
target: 'http://10.0.10.98:2060',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.5.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-helmet": "^6.1.0",
|
||||
"react-helmet-async": "^1.3.0",
|
||||
"react-i18next": "^11.18.3",
|
||||
"react-router-dom": "^6.4.0",
|
||||
"swr": "^1.3.0",
|
||||
|
|
|
@ -67,7 +67,7 @@ specifiers:
|
|||
react-app-rewired: ^2.2.1
|
||||
react-bootstrap: ^2.5.0
|
||||
react-dom: ^18.2.0
|
||||
react-helmet: ^6.1.0
|
||||
react-helmet-async: ^1.3.0
|
||||
react-i18next: ^11.18.3
|
||||
react-router-dom: ^6.4.0
|
||||
react-scripts: 5.0.1
|
||||
|
@ -102,7 +102,7 @@ dependencies:
|
|||
react: 18.2.0
|
||||
react-bootstrap: 2.5.0_7ey2zzynotv32rpkwno45fsx4e
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-helmet: 6.1.0_react@18.2.0
|
||||
react-helmet-async: 1.3.0_biqbaboplfbrettd7655fr4n2y
|
||||
react-i18next: 11.18.6_ulhmqqxshznzmtuvahdi5nasbq
|
||||
react-router-dom: 6.4.0_biqbaboplfbrettd7655fr4n2y
|
||||
swr: 1.3.0_react@18.2.0
|
||||
|
@ -9581,16 +9581,19 @@ packages:
|
|||
resolution: {integrity: sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==}
|
||||
dev: false
|
||||
|
||||
/react-helmet/6.1.0_react@18.2.0:
|
||||
resolution: {integrity: sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==}
|
||||
/react-helmet-async/1.3.0_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-9jZ57/dAn9t3q6hneQS0wukqC2ENOBgMNVEhb/ZG9ZSxUetzVIw4iAmEU38IaVg3QGYauQPhSeUTuIUtFglWpg==}
|
||||
peerDependencies:
|
||||
react: '>=16.3.0'
|
||||
react: ^16.6.0 || ^17.0.0 || ^18.0.0
|
||||
react-dom: ^16.6.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
object-assign: 4.1.1
|
||||
'@babel/runtime': 7.19.0
|
||||
invariant: 2.2.4
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0_react@18.2.0
|
||||
react-fast-compare: 3.2.0
|
||||
react-side-effect: 2.1.2_react@18.2.0
|
||||
shallowequal: 1.1.0
|
||||
dev: false
|
||||
|
||||
/react-i18next/11.18.6_ulhmqqxshznzmtuvahdi5nasbq:
|
||||
|
@ -9747,14 +9750,6 @@ packages:
|
|||
- webpack-hot-middleware
|
||||
- webpack-plugin-serve
|
||||
|
||||
/react-side-effect/2.1.2_react@18.2.0:
|
||||
resolution: {integrity: sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==}
|
||||
peerDependencies:
|
||||
react: ^16.3.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/react-transition-group/4.4.5_biqbaboplfbrettd7655fr4n2y:
|
||||
resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==}
|
||||
peerDependencies:
|
||||
|
@ -10257,6 +10252,10 @@ packages:
|
|||
/setprototypeof/1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
/shallowequal/1.1.0:
|
||||
resolution: {integrity: sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==}
|
||||
dev: false
|
||||
|
||||
/shebang-command/2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
|
@ -80,7 +80,7 @@ export interface RegisterReqParams extends LoginReqParams {
|
|||
name: string;
|
||||
}
|
||||
|
||||
export interface ModifyPassReq {
|
||||
export interface ModifyPasswordReq {
|
||||
old_pass: string;
|
||||
pass: string;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ export interface ImgCodeRes {
|
|||
verify: boolean;
|
||||
}
|
||||
|
||||
export interface PssRetReq extends ImgCodeReq {
|
||||
export interface PasswordResetReq extends ImgCodeReq {
|
||||
e_mail: string;
|
||||
}
|
||||
|
||||
|
@ -145,11 +145,11 @@ export interface CheckImgReq {
|
|||
action: 'login' | 'e_mail' | 'find_pass';
|
||||
}
|
||||
|
||||
export interface NoticeSetReq {
|
||||
export interface SetNoticeReq {
|
||||
notice_switch: boolean;
|
||||
}
|
||||
|
||||
export interface QuDetailRes {
|
||||
export interface QuestionDetailRes {
|
||||
id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
|
|
|
@ -5,23 +5,35 @@ import { Avatar } from '@answer/components';
|
|||
|
||||
interface Props {
|
||||
data: any;
|
||||
showAvatar?: boolean;
|
||||
avatarSize?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Index: FC<Props> = ({
|
||||
data,
|
||||
showAvatar = true,
|
||||
avatarSize = '20px',
|
||||
className = 'fs-14',
|
||||
}) => {
|
||||
return (
|
||||
<div className={`text-secondary ${className}`}>
|
||||
<Link to={`/users/${data?.username}`}>
|
||||
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
||||
</Link>
|
||||
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
||||
{data?.display_name}
|
||||
</Link>
|
||||
{data.status !== 'deleted' ? (
|
||||
<Link to={`/users/${data?.username}`}>
|
||||
{showAvatar && (
|
||||
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
||||
)}
|
||||
<span className="me-1 text-break">{data?.display_name}</span>
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
{showAvatar && (
|
||||
<Avatar avatar={data?.avatar} size={avatarSize} className="me-1" />
|
||||
)}
|
||||
<span className="me-1 text-break">{data?.display_name}</span>
|
||||
</>
|
||||
)}
|
||||
|
||||
<span className="fw-bold">{data?.rank}</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -17,15 +17,20 @@ const ActionBar = ({
|
|||
onReply,
|
||||
onVote,
|
||||
onAction,
|
||||
userStatus = '',
|
||||
}) => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'comment' });
|
||||
|
||||
return (
|
||||
<div className="d-flex justify-content-between fs-14">
|
||||
<div className="d-flex align-items-center">
|
||||
<Link to={`/users/${username}`}>{nickName}</Link>
|
||||
<span className="mx-1 text-secondary">•</span>
|
||||
<FormatTime time={createdAt} className="text-secondary me-3" />
|
||||
<div className="d-flex align-items-center text-secondary">
|
||||
{userStatus !== 'deleted' ? (
|
||||
<Link to={`/users/${username}`}>{nickName}</Link>
|
||||
) : (
|
||||
<span>{nickName}</span>
|
||||
)}
|
||||
<span className="mx-1">•</span>
|
||||
<FormatTime time={createdAt} className="me-3" />
|
||||
<Button
|
||||
variant="link"
|
||||
size="sm"
|
||||
|
|
|
@ -36,7 +36,7 @@ const Form = ({
|
|||
<Mentions pageUsers={pageUsers.getUsers()}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="text-muted fs-14">{t(`tip_${mode}`)}</div>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
</div>
|
||||
{type === 'edit' ? (
|
||||
<div className="d-flex flex-column">
|
||||
|
|
|
@ -22,7 +22,7 @@ const Form = ({ userName, onSendReply, onCancel, mode }) => {
|
|||
<Mentions pageUsers={pageUsers.getUsers()}>
|
||||
<TextArea size="sm" value={value} onChange={handleChange} />
|
||||
</Mentions>
|
||||
<div className="text-muted fs-14">{t(`tip_${mode}`)}</div>
|
||||
<div className="form-text">{t(`tip_${mode}`)}</div>
|
||||
</div>
|
||||
<div className="d-flex flex-column">
|
||||
<Button
|
||||
|
|
|
@ -269,6 +269,7 @@ const Comment = ({ objectId, mode }) => {
|
|||
voteCount={item.vote_count}
|
||||
isVote={item.is_vote}
|
||||
memberActions={item.member_actions}
|
||||
userStatus={item.user_status}
|
||||
onReply={() => {
|
||||
handleReply(item.comment_id);
|
||||
}}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { useSearchParams, NavLink, Link, useNavigate } from 'react-router-dom';
|
|||
|
||||
import { Avatar, Icon } from '@answer/components';
|
||||
import { userInfoStore, siteInfoStore, interfaceStore } from '@answer/stores';
|
||||
import { logout, useQueryNotificationRedDot } from '@answer/api';
|
||||
import { logout, useQueryNotificationStatus } from '@answer/api';
|
||||
import Storage from '@answer/utils/storage';
|
||||
|
||||
import './index.scss';
|
||||
|
@ -28,7 +28,7 @@ const Header: FC = () => {
|
|||
const [searchStr, setSearch] = useState('');
|
||||
const siteInfo = siteInfoStore((state) => state.siteInfo);
|
||||
const { interface: interfaceInfo } = interfaceStore();
|
||||
const { data: redDot } = useQueryNotificationRedDot();
|
||||
const { data: redDot } = useQueryNotificationStatus();
|
||||
const handleInput = (val) => {
|
||||
setSearch(val);
|
||||
};
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import { Modal } from '@answer/components';
|
||||
import { useReportModal, useToast } from '@answer/hooks';
|
||||
import { questionDelete, answerDelete } from '@answer/api';
|
||||
import { deleteQuestion, deleteAnswer } from '@answer/api';
|
||||
import { isLogin } from '@answer/utils';
|
||||
import Share from '../Share';
|
||||
|
||||
|
@ -61,7 +61,7 @@ const Index: FC<IProps> = ({
|
|||
confirmBtnVariant: 'danger',
|
||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||
onConfirm: () => {
|
||||
questionDelete({
|
||||
deleteQuestion({
|
||||
id: qid,
|
||||
}).then(() => {
|
||||
toast.onShow({
|
||||
|
@ -82,7 +82,7 @@ const Index: FC<IProps> = ({
|
|||
confirmBtnVariant: 'danger',
|
||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||
onConfirm: () => {
|
||||
answerDelete({
|
||||
deleteAnswer({
|
||||
id: aid,
|
||||
}).then(() => {
|
||||
// refersh page
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { FC } from 'react';
|
||||
import { Pagination } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
|
@ -47,6 +47,7 @@ const PageItem = ({ page, currentPage, path }: PageItemProps) => {
|
|||
href={path}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigate(path);
|
||||
window.scrollTo(0, 0);
|
||||
}}>
|
||||
|
@ -111,7 +112,7 @@ const Index: FC<Props> = ({
|
|||
)}
|
||||
{currentPage === 4 && totalPage > 6 && (
|
||||
<PageItem
|
||||
key="6"
|
||||
key="page6"
|
||||
page={6}
|
||||
currentPage={currentPage}
|
||||
path={handleParams(6)}
|
||||
|
@ -133,13 +134,13 @@ const Index: FC<Props> = ({
|
|||
{currentPage >= 5 && (
|
||||
<>
|
||||
<PageItem
|
||||
key="prev2"
|
||||
key={realPage - 2}
|
||||
page={realPage - 2}
|
||||
currentPage={currentPage}
|
||||
path={handleParams(realPage - 2)}
|
||||
/>
|
||||
<PageItem
|
||||
key="prev1"
|
||||
key={realPage - 1}
|
||||
page={realPage - 1}
|
||||
currentPage={currentPage}
|
||||
path={handleParams(realPage - 1)}
|
||||
|
@ -194,4 +195,4 @@ const Index: FC<Props> = ({
|
|||
);
|
||||
};
|
||||
|
||||
export default memo(Index);
|
||||
export default Index;
|
||||
|
|
|
@ -5,7 +5,14 @@ import { useTranslation } from 'react-i18next';
|
|||
|
||||
import { useQuestionList } from '@answer/api';
|
||||
import type * as Type from '@answer/common/interface';
|
||||
import { Icon, Tag, Pagination, FormatTime, Empty } from '@answer/components';
|
||||
import {
|
||||
Icon,
|
||||
Tag,
|
||||
Pagination,
|
||||
FormatTime,
|
||||
Empty,
|
||||
BaseUserCard,
|
||||
} from '@answer/components';
|
||||
|
||||
const QuestionOrderKeys: Type.QuestionOrderBy[] = [
|
||||
'newest',
|
||||
|
@ -25,12 +32,11 @@ const QuestionLastUpdate = ({ q }) => {
|
|||
// question answered
|
||||
return (
|
||||
<>
|
||||
<a
|
||||
className="p-0"
|
||||
href={`/users/${q.last_answered_user_info.username}`}>
|
||||
{q.last_answered_user_info.display_name}
|
||||
</a>
|
||||
<span className="fw-bold px-1">{q.last_answered_user_info.rank}</span>
|
||||
<BaseUserCard
|
||||
data={q.last_answered_user_info}
|
||||
showAvatar={false}
|
||||
className="me-1"
|
||||
/>
|
||||
•
|
||||
<FormatTime
|
||||
time={q.update_time}
|
||||
|
@ -45,10 +51,11 @@ const QuestionLastUpdate = ({ q }) => {
|
|||
// question modified
|
||||
return (
|
||||
<>
|
||||
<a className="p-0" href={`/users/${q.update_user_info.username}`}>
|
||||
{q.update_user_info.display_name}
|
||||
</a>
|
||||
<span className="fw-bold px-1">{q.update_user_info.rank}</span>
|
||||
<BaseUserCard
|
||||
data={q.update_user_info}
|
||||
showAvatar={false}
|
||||
className="me-1"
|
||||
/>
|
||||
•
|
||||
<FormatTime
|
||||
time={q.edit_time}
|
||||
|
@ -62,10 +69,7 @@ const QuestionLastUpdate = ({ q }) => {
|
|||
// default: asked
|
||||
return (
|
||||
<>
|
||||
<a className="p-0" href={`/users/${q.user_info.username}`}>
|
||||
{q.user_info.display_name}
|
||||
</a>
|
||||
<strong className="px-1">{q.user_info.rank}</strong>
|
||||
<BaseUserCard data={q.user_info} showAvatar={false} className="me-1" />
|
||||
•
|
||||
<FormatTime
|
||||
time={q.create_time}
|
||||
|
|
|
@ -50,7 +50,7 @@ const Index: FC<IProps> = ({ type, qid, aid, title }) => {
|
|||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
if (window.navigator?.canShare?.({ text: '111' })) {
|
||||
if (window.navigator?.canShare?.({ text: 'can_share' })) {
|
||||
setSystemShareState(true);
|
||||
}
|
||||
}, []);
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||
import { Button, Col } from 'react-bootstrap';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import { emailReSend, checkImgCode } from '@answer/api';
|
||||
import { resendEmail, checkImgCode } from '@answer/api';
|
||||
import { PicAuthCodeModal } from '@answer/components/Modal';
|
||||
import type {
|
||||
ImgCodeRes,
|
||||
|
@ -53,7 +53,7 @@ const Index: React.FC<IProps> = ({ visible = false }) => {
|
|||
captcha_id: imgCode.captcha_id,
|
||||
};
|
||||
}
|
||||
emailReSend(obj)
|
||||
resendEmail(obj)
|
||||
.then(() => {
|
||||
setSuccess(true);
|
||||
setModalState(false);
|
||||
|
|
|
@ -12,14 +12,22 @@ interface Props {
|
|||
const Index: FC<Props> = ({ data, time, preFix }) => {
|
||||
return (
|
||||
<div className="d-flex">
|
||||
<Link to={`/users/${data?.username}`}>
|
||||
{data?.status !== 'deleted' ? (
|
||||
<Link to={`/users/${data?.username}`}>
|
||||
<Avatar avatar={data?.avatar} size="40px" className="me-2" />
|
||||
</Link>
|
||||
) : (
|
||||
<Avatar avatar={data?.avatar} size="40px" className="me-2" />
|
||||
</Link>
|
||||
)}
|
||||
<div className="fs-14 text-secondary">
|
||||
<div>
|
||||
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
||||
{data?.display_name}
|
||||
</Link>
|
||||
{data?.status !== 'deleted' ? (
|
||||
<Link to={`/users/${data?.username}`} className="me-1 text-break">
|
||||
{data?.display_name}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="me-1 text-break">{data?.display_name}</span>
|
||||
)}
|
||||
<span className="fw-bold">{data?.rank}</span>
|
||||
</div>
|
||||
{time && <FormatTime time={time} preFix={preFix} />}
|
||||
|
|
|
@ -848,7 +848,7 @@
|
|||
"interface": {
|
||||
"page_title": "Interface",
|
||||
"logo": {
|
||||
"label": "Logo",
|
||||
"label": "Logo (optional)",
|
||||
"msg": "Site logo cannot be empty.",
|
||||
"text": "You can upload your image or <1>reset</1> it to the site title text."
|
||||
},
|
||||
|
|
|
@ -30,7 +30,7 @@ const answerFilterItems: Type.AdminContentsFilterBy[] = ['normal', 'deleted'];
|
|||
const Answers: FC = () => {
|
||||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||
const curFilter = urlSearchParams.get('status') || answerFilterItems[0];
|
||||
const pageSize = 20;
|
||||
const PAGE_SIZE = 20;
|
||||
const curPage = Number(urlSearchParams.get('page')) || 1;
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'admin.answers' });
|
||||
|
||||
|
@ -39,7 +39,7 @@ const Answers: FC = () => {
|
|||
isLoading,
|
||||
mutate: refreshList,
|
||||
} = useAnswerSearch({
|
||||
page_size: pageSize,
|
||||
page_size: PAGE_SIZE,
|
||||
page: curPage,
|
||||
status: curFilter as Type.AdminContentsFilterBy,
|
||||
});
|
||||
|
@ -189,7 +189,7 @@ const Answers: FC = () => {
|
|||
<Pagination
|
||||
currentPage={curPage}
|
||||
totalSize={count}
|
||||
pageSize={pageSize}
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -23,14 +23,14 @@ const Flags: FC = () => {
|
|||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||
const curFilter = urlSearchParams.get('status') || flagFilterKeys[0];
|
||||
const curType = urlSearchParams.get('type') || flagTypeKeys[0];
|
||||
const pageSize = 20;
|
||||
const PAGE_SIZE = 20;
|
||||
const curPage = Number(urlSearchParams.get('page')) || 1;
|
||||
const {
|
||||
data: listData,
|
||||
isLoading,
|
||||
mutate: refreshList,
|
||||
} = useFlagSearch({
|
||||
page_size: pageSize,
|
||||
page_size: PAGE_SIZE,
|
||||
page: curPage,
|
||||
status: curFilter as Type.FlagStatus,
|
||||
object_type: curType as Type.FlagType,
|
||||
|
@ -159,7 +159,7 @@ const Flags: FC = () => {
|
|||
<Pagination
|
||||
currentPage={curPage}
|
||||
totalSize={count}
|
||||
pageSize={pageSize}
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { useEditStatusModal, useReportModal } from '@answer/hooks';
|
|||
import {
|
||||
useQuestionSearch,
|
||||
changeQuestionStatus,
|
||||
questionDelete,
|
||||
deleteQuestion,
|
||||
} from '@answer/api';
|
||||
import * as Type from '@answer/common/interface';
|
||||
|
||||
|
@ -35,7 +35,7 @@ const questionFilterItems: Type.AdminContentsFilterBy[] = [
|
|||
'deleted',
|
||||
];
|
||||
|
||||
const pageSize = 20;
|
||||
const PAGE_SIZE = 20;
|
||||
const Questions: FC = () => {
|
||||
const [urlSearchParams, setUrlSearchParams] = useSearchParams();
|
||||
const curFilter = urlSearchParams.get('status') || questionFilterItems[0];
|
||||
|
@ -47,7 +47,7 @@ const Questions: FC = () => {
|
|||
isLoading,
|
||||
mutate: refreshList,
|
||||
} = useQuestionSearch({
|
||||
page_size: pageSize,
|
||||
page_size: PAGE_SIZE,
|
||||
page: curPage,
|
||||
status: curFilter as Type.AdminContentsFilterBy,
|
||||
});
|
||||
|
@ -89,7 +89,7 @@ const Questions: FC = () => {
|
|||
confirmBtnVariant: 'danger',
|
||||
confirmText: t('delete', { keyPrefix: 'btns' }),
|
||||
onConfirm: () => {
|
||||
questionDelete({
|
||||
deleteQuestion({
|
||||
id,
|
||||
}).then(() => {
|
||||
refreshList();
|
||||
|
@ -207,7 +207,7 @@ const Questions: FC = () => {
|
|||
<Pagination
|
||||
currentPage={curPage}
|
||||
totalSize={count}
|
||||
pageSize={pageSize}
|
||||
pageSize={PAGE_SIZE}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
import { FC } from 'react';
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Button,
|
||||
Table,
|
||||
Figure,
|
||||
Stack,
|
||||
} from 'react-bootstrap';
|
||||
|
||||
import { AccordionNav } from '@answer/components';
|
||||
import { ADMIN_NAV_MENUS } from '@answer/common/constants';
|
||||
|
||||
import '../index.scss';
|
||||
|
||||
const UserOverview: FC = () => {
|
||||
return (
|
||||
<Container className="admin-container">
|
||||
<Row>
|
||||
<Col lg={2}>
|
||||
<AccordionNav menus={ADMIN_NAV_MENUS} />
|
||||
</Col>
|
||||
<Col lg={10}>
|
||||
<Button variant="outline-secondary" size="sm">
|
||||
← Back
|
||||
</Button>
|
||||
<h5 className="mb-3 mt-4">Profile</h5>
|
||||
<Table className="mb-5">
|
||||
<tbody className="align-middle">
|
||||
<tr>
|
||||
<td>ID</td>
|
||||
<td>1030000000091295</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Display name</td>
|
||||
<td>Jim Green</td>
|
||||
<td />
|
||||
</tr>
|
||||
<tr>
|
||||
<td>username</td>
|
||||
<td>jimgreen</td>
|
||||
<td>
|
||||
<Button variant="link" size="sm">
|
||||
Edit
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Profile image</td>
|
||||
<td>
|
||||
<Figure.Image
|
||||
width={48}
|
||||
height={48}
|
||||
className="rounded-1 m-0"
|
||||
src="https://gw.alicdn.com/bao/uploaded/i4/1607723262/O1CN01JJCGVD1Zy2jryOhDc_!!1607723262.jpg"
|
||||
/>
|
||||
</td>
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
<h5 className="mb-3 mt-4">Permissions</h5>
|
||||
<Table className="mb-5">
|
||||
<tbody className="align-middle">
|
||||
<tr>
|
||||
<td>Activated</td>
|
||||
<td>No</td>
|
||||
<td>
|
||||
<Button size="sm" variant="link">
|
||||
Activate
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Admin?</td>
|
||||
<td>No</td>
|
||||
<td>
|
||||
<Button size="sm" variant="link">
|
||||
Grant
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Suspended?</td>
|
||||
<td>No</td>
|
||||
<td>
|
||||
<Stack direction="horizontal" gap={1}>
|
||||
<Button size="sm" variant="link" className="text-danger">
|
||||
Suspend
|
||||
</Button>
|
||||
<div className="text-secondary text-nowrap">
|
||||
A suspended user can't log in
|
||||
</div>
|
||||
</Stack>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
<h5 className="mb-3 mt-4">Activity</h5>
|
||||
<Table className="mb-5">
|
||||
<tbody className="align-middle">
|
||||
<tr>
|
||||
<td>Reputation</td>
|
||||
<td>1805</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Answers</td>
|
||||
<td>30</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Questions</td>
|
||||
<td>10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Created</td>
|
||||
<td>Sep 1, 2022 at 16:00</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Registration IP address</td>
|
||||
<td>11.22.33.44</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Seen</td>
|
||||
<td>Sep 6, 2022 at 09:35</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Last IP address</td>
|
||||
<td>11.22.33.44</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserOverview;
|
|
@ -1,7 +1,7 @@
|
|||
import { FC, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Outlet } from 'react-router-dom';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
|
||||
import { SWRConfig } from 'swr';
|
||||
|
||||
|
@ -56,11 +56,9 @@ const Layout: FC = () => {
|
|||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<HelmetProvider>
|
||||
<Helmet>
|
||||
{siteInfo ? (
|
||||
<meta name="description" content={siteInfo.description} />
|
||||
) : null}
|
||||
{siteInfo && <meta name="description" content={siteInfo.description} />}
|
||||
</Helmet>
|
||||
<SWRConfig
|
||||
value={{
|
||||
|
@ -74,7 +72,7 @@ const Layout: FC = () => {
|
|||
<Toast msg={toastMsg} variant={variant} onClose={closeToast} />
|
||||
<Footer />
|
||||
</SWRConfig>
|
||||
</>
|
||||
</HelmetProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -20,49 +20,26 @@ import type * as Type from '@answer/common/interface';
|
|||
import SearchQuestion from './components/SearchQuestion';
|
||||
|
||||
interface FormDataItem {
|
||||
title: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
focus?: boolean;
|
||||
};
|
||||
tags: {
|
||||
value: Type.Tag[];
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
focus?: boolean;
|
||||
};
|
||||
content: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
focus?: boolean;
|
||||
};
|
||||
answer: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
focus?: boolean;
|
||||
};
|
||||
title: Type.FormValue<string>;
|
||||
tags: Type.FormValue<Type.Tag[]>;
|
||||
content: Type.FormValue<string>;
|
||||
answer: Type.FormValue<string>;
|
||||
}
|
||||
const initFormData = {
|
||||
title: {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
focus: false,
|
||||
},
|
||||
tags: {
|
||||
value: [],
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
focus: false,
|
||||
},
|
||||
content: {
|
||||
value: '',
|
||||
isInvalid: false,
|
||||
errorMsg: '',
|
||||
focus: false,
|
||||
},
|
||||
answer: {
|
||||
value: '',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { memo, FC } from 'react';
|
||||
import { ButtonGroup } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
|
||||
interface Props {
|
||||
count: number;
|
||||
|
@ -11,6 +11,7 @@ const Index: FC<Props> = ({ count = 0, order = 'default' }) => {
|
|||
const { t } = useTranslation('translation', {
|
||||
keyPrefix: 'question_detail.answers',
|
||||
});
|
||||
const location = useLocation();
|
||||
return (
|
||||
<div
|
||||
className="d-flex align-items-center justify-content-between mt-5 mb-3"
|
||||
|
@ -20,14 +21,14 @@ const Index: FC<Props> = ({ count = 0, order = 'default' }) => {
|
|||
</h5>
|
||||
<ButtonGroup size="sm">
|
||||
<Link
|
||||
to={`${window.location.pathname}?order=default`}
|
||||
to={`${location.pathname}?order=default`}
|
||||
className={`btn btn-outline-secondary ${
|
||||
order !== 'updated' ? 'active' : ''
|
||||
}`}>
|
||||
{t('score')}
|
||||
</Link>
|
||||
<Link
|
||||
to={`${window.location.pathname}?order=updated`}
|
||||
to={`${location.pathname}?order=updated`}
|
||||
className={`btn btn-outline-secondary ${
|
||||
order === 'updated' ? 'active' : ''
|
||||
}`}>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Container, Row, Col } from 'react-bootstrap';
|
||||
import { useParams, useSearchParams } from 'react-router-dom';
|
||||
import { useParams, useSearchParams, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { questionDetail, getAnswers } from '@answer/api';
|
||||
import { Pagination, PageTitle } from '@answer/components';
|
||||
|
@ -9,7 +9,7 @@ import { scrollTop } from '@answer/utils';
|
|||
import { usePageUsers } from '@answer/hooks';
|
||||
import type {
|
||||
ListResult,
|
||||
QuDetailRes,
|
||||
QuestionDetailRes,
|
||||
AnswerItem,
|
||||
} from '@answer/common/interface';
|
||||
|
||||
|
@ -25,12 +25,13 @@ import {
|
|||
import './index.scss';
|
||||
|
||||
const Index = () => {
|
||||
const navigate = useNavigate();
|
||||
const { qid = '', aid = '' } = useParams();
|
||||
const [urlSearch] = useSearchParams();
|
||||
|
||||
const page = Number(urlSearch.get('page') || 0);
|
||||
const order = urlSearch.get('order') || '';
|
||||
const [question, setQuestion] = useState<QuDetailRes | null>(null);
|
||||
const [question, setQuestion] = useState<QuestionDetailRes | null>(null);
|
||||
const [answers, setAnswers] = useState<ListResult<AnswerItem>>({
|
||||
count: -1,
|
||||
list: [],
|
||||
|
@ -75,7 +76,7 @@ const Index = () => {
|
|||
const initPage = (type: string) => {
|
||||
if (type === 'delete_question') {
|
||||
setTimeout(() => {
|
||||
window.history.back();
|
||||
navigate(-1);
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,16 +17,8 @@ import type * as Type from '@answer/common/interface';
|
|||
import './index.scss';
|
||||
|
||||
interface FormDataItem {
|
||||
answer: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
description: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
answer: Type.FormValue<string>;
|
||||
description: Type.FormValue<string>;
|
||||
}
|
||||
const initFormData = {
|
||||
answer: {
|
||||
|
@ -111,7 +103,7 @@ const Ask = () => {
|
|||
id: aid,
|
||||
};
|
||||
modifyAnswer(params).then(() => {
|
||||
window.location.href = `/questions/${qid}/${aid}`;
|
||||
navigate(`/questions/${qid}/${aid}`);
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { ListGroupItem, ButtonGroup, Button } from 'react-bootstrap';
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const sortBtns = [
|
||||
|
@ -21,6 +21,7 @@ interface Props {
|
|||
}
|
||||
const Index: FC<Props> = ({ sort, count = 0 }) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { t } = useTranslation('translation', {
|
||||
|
@ -28,7 +29,7 @@ const Index: FC<Props> = ({ sort, count = 0 }) => {
|
|||
});
|
||||
|
||||
const handleParams = (order): string => {
|
||||
const basePath = window.location.pathname;
|
||||
const basePath = location.pathname;
|
||||
searchParams.delete('page');
|
||||
searchParams.set('order', order);
|
||||
const searchStr = searchParams.toString();
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { memo, FC } from 'react';
|
||||
import { ListGroupItem, Badge } from 'react-bootstrap';
|
||||
|
||||
import { Icon, Tag, FormatTime } from '@answer/components';
|
||||
import { Icon, Tag, FormatTime, BaseUserCard } from '@answer/components';
|
||||
import type { SearchResItem } from '@answer/common/interface';
|
||||
import { formatCount } from '@answer/utils';
|
||||
|
||||
interface Props {
|
||||
data: SearchResItem;
|
||||
|
@ -28,14 +27,8 @@ const Index: FC<Props> = ({ data }) => {
|
|||
</a>
|
||||
</div>
|
||||
<div className="d-flex flex-wrap align-items-center fs-14 text-secondary mb-2">
|
||||
<a href={`/users/${data.object?.user_info?.username}`}>
|
||||
{data.object?.user_info?.display_name}
|
||||
</a>
|
||||
{data.object?.user_info?.rank > 0 && (
|
||||
<span className="fw-bold ms-1">
|
||||
{formatCount(data.object.user_info.rank)}
|
||||
</span>
|
||||
)}
|
||||
<BaseUserCard data={data.object?.user_info} showAvatar={false} />
|
||||
|
||||
<span className="split-dot" />
|
||||
<FormatTime
|
||||
time={data.object?.created_at}
|
||||
|
|
|
@ -9,28 +9,13 @@ import classNames from 'classnames';
|
|||
import { Editor, EditorRef, PageTitle } from '@answer/components';
|
||||
import { useTagInfo, modifyTag, useQueryRevisions } from '@answer/api';
|
||||
import { userInfoStore } from '@answer/stores';
|
||||
import type * as Type from '@answer/common/interface';
|
||||
|
||||
interface FormDataItem {
|
||||
displayName: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
slugName: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
description: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
editSummary: {
|
||||
value: string;
|
||||
isInvalid: boolean;
|
||||
errorMsg: string;
|
||||
};
|
||||
displayName: Type.FormValue<string>;
|
||||
slugName: Type.FormValue<string>;
|
||||
description: Type.FormValue<string>;
|
||||
editSummary: Type.FormValue<string>;
|
||||
}
|
||||
const initFormData = {
|
||||
displayName: {
|
||||
|
|
|
@ -2,10 +2,10 @@ import { FC, memo, useEffect, useState } from 'react';
|
|||
import { Form, Button } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { passRetrieve, checkImgCode } from '@answer/api';
|
||||
import { resetPassword, checkImgCode } from '@answer/api';
|
||||
import type {
|
||||
ImgCodeRes,
|
||||
PssRetReq,
|
||||
PasswordResetReq,
|
||||
FormDataType,
|
||||
} from '@answer/common/interface';
|
||||
|
||||
|
@ -70,7 +70,7 @@ const Index: FC<IProps> = ({ visible = false, callback }) => {
|
|||
if (e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
const params: PssRetReq = {
|
||||
const params: PasswordResetReq = {
|
||||
e_mail: formData.e_mail.value,
|
||||
};
|
||||
if (imgCode.verify) {
|
||||
|
@ -78,7 +78,7 @@ const Index: FC<IProps> = ({ visible = false, callback }) => {
|
|||
params.captcha_id = imgCode.captcha_id;
|
||||
}
|
||||
|
||||
passRetrieve(params)
|
||||
resetPassword(params)
|
||||
.then(() => {
|
||||
callback?.(2, formData.e_mail.value);
|
||||
setModalState(false);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { FC, memo, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { accountActivate } from '@answer/api';
|
||||
import { activateAccount } from '@answer/api';
|
||||
import { userInfoStore } from '@answer/stores';
|
||||
import { getQueryString } from '@answer/utils';
|
||||
|
||||
|
@ -14,10 +14,10 @@ const Index: FC = () => {
|
|||
const code = getQueryString('code');
|
||||
|
||||
if (code) {
|
||||
accountActivate(encodeURIComponent(code)).then((res) => {
|
||||
activateAccount(encodeURIComponent(code)).then((res) => {
|
||||
updateUser(res);
|
||||
setTimeout(() => {
|
||||
window.location.href = '/users/account-activation/success';
|
||||
window.location.replace('/users/account-activation/success');
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -108,13 +108,9 @@ const Index: React.FC = () => {
|
|||
setRefresh((pre) => pre + 1);
|
||||
}
|
||||
if (res.mail_status === 1) {
|
||||
const path = Storage.get('ANSWER_PATH');
|
||||
const path = Storage.get('ANSWER_PATH') || '/';
|
||||
Storage.remove('ANSWER_PATH');
|
||||
if (path) {
|
||||
window.location.href = path;
|
||||
} else {
|
||||
window.location.href = '/';
|
||||
}
|
||||
window.location.replace(path);
|
||||
}
|
||||
|
||||
setModalState(false);
|
||||
|
|
|
@ -37,9 +37,13 @@ const Inbox = ({ data, handleReadNotification }) => {
|
|||
key={item.id}
|
||||
className={classNames('py-3', !item.is_read && 'warning')}>
|
||||
<div>
|
||||
<Link to={`/users/${item.user_info.username}`}>
|
||||
{item.user_info.display_name}
|
||||
</Link>{' '}
|
||||
{item.user_info.status !== 'deleted' ? (
|
||||
<Link to={`/users/${item.user_info.username}`}>
|
||||
{item.user_info.display_name}{' '}
|
||||
</Link>
|
||||
) : (
|
||||
<span>{item.user_info.display_name} </span>
|
||||
)}
|
||||
{item.notification_action}{' '}
|
||||
<Link to={url} onClick={() => handleReadNotification(item.id)}>
|
||||
{item.object_info.title}
|
||||
|
|
|
@ -5,8 +5,8 @@ import { useParams, useNavigate } from 'react-router-dom';
|
|||
|
||||
import {
|
||||
useQueryNotifications,
|
||||
clearUnReadNotification,
|
||||
clearNotificationRedDot,
|
||||
clearUnreadNotification,
|
||||
clearNotificationStatus,
|
||||
readNotification,
|
||||
} from '@answer/api';
|
||||
import { PageTitle } from '@answer/components';
|
||||
|
@ -29,7 +29,7 @@ const Notifications = () => {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
clearNotificationRedDot(type);
|
||||
clearNotificationStatus(type);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -56,7 +56,7 @@ const Notifications = () => {
|
|||
};
|
||||
|
||||
const handleUnreadNotification = async () => {
|
||||
await clearUnReadNotification(type);
|
||||
await clearUnreadNotification(type);
|
||||
mutate();
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Container, Col, Form, Button } from 'react-bootstrap';
|
|||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { passRetrieveSet } from '@answer/api';
|
||||
import { replacementPassword } from '@answer/api';
|
||||
import { userInfoStore } from '@answer/stores';
|
||||
import { getQueryString, isLogin } from '@answer/utils';
|
||||
import type { FormDataType } from '@answer/common/interface';
|
||||
|
@ -98,7 +98,7 @@ const Index: React.FC = () => {
|
|||
console.error('code is required');
|
||||
return;
|
||||
}
|
||||
passRetrieveSet({
|
||||
replacementPassword({
|
||||
code: encodeURIComponent(code),
|
||||
pass: formData.pass.value,
|
||||
})
|
||||
|
|
|
@ -2,7 +2,7 @@ import { FC, memo } from 'react';
|
|||
import { ListGroup, ListGroupItem } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Icon, FormatTime, Tag } from '@answer/components';
|
||||
import { Icon, FormatTime, Tag, BaseUserCard } from '@answer/components';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
|
@ -31,15 +31,10 @@ const Index: FC<Props> = ({ visible, tabName, data }) => {
|
|||
</h6>
|
||||
<div className="d-flex align-items-center fs-14 text-secondary mb-2">
|
||||
{tabName === 'bookmarks' && (
|
||||
<div className="d-flex">
|
||||
<a
|
||||
href={`/users/${item.user_info?.username}`}
|
||||
className="me-1">
|
||||
{item.user_info?.display_name}
|
||||
</a>
|
||||
<strong>{item.user_info?.rank}</strong>
|
||||
<>
|
||||
<BaseUserCard data={item.user_info} showAvatar={false} />
|
||||
<span className="split-dot" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<FormatTime
|
||||
time={item.create_time}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { ButtonGroup, Button } from 'react-bootstrap';
|
||||
import { useSearchParams, useNavigate } from 'react-router-dom';
|
||||
import { useSearchParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const sortBtns = [
|
||||
|
@ -26,10 +26,11 @@ const Index: FC<Props> = ({
|
|||
}) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'personal' });
|
||||
|
||||
const handleParams = (order): string => {
|
||||
const basePath = window.location.pathname;
|
||||
const basePath = location.pathname;
|
||||
searchParams.delete('page');
|
||||
searchParams.set('order', order);
|
||||
const searchStr = searchParams.toString();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { FC, memo } from 'react';
|
||||
import { Badge, OverlayTrigger, Tooltip } from 'react-bootstrap';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Avatar, Icon } from '@answer/components';
|
||||
import type { UserInfoRes } from '@answer/common/interface';
|
||||
|
@ -16,14 +17,26 @@ const Index: FC<Props> = ({ data }) => {
|
|||
}
|
||||
return (
|
||||
<div className="d-flex mb-4">
|
||||
<a href={`/users/${data.username}`}>
|
||||
{data?.status !== 'deleted' ? (
|
||||
<Link to={`/users/${data.username}`} reloadDocument>
|
||||
<Avatar avatar={data.avatar} size="160px" />
|
||||
</Link>
|
||||
) : (
|
||||
<Avatar avatar={data.avatar} size="160px" />
|
||||
</a>
|
||||
)}
|
||||
|
||||
<div className="ms-4">
|
||||
<div className="d-flex align-items-center mb-2">
|
||||
<a href={`/users/${data.username}`} className="text-body h3 mb-0">
|
||||
{data.display_name}
|
||||
</a>
|
||||
{data?.status !== 'deleted' ? (
|
||||
<Link
|
||||
to={`/users/${data.username}`}
|
||||
className="text-body h3 mb-0"
|
||||
reloadDocument>
|
||||
{data.display_name}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="text-body h3 mb-0">{data.display_name}</span>
|
||||
)}
|
||||
{data?.is_admin && (
|
||||
<div className="ms-2">
|
||||
<OverlayTrigger
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Form, Button } from 'react-bootstrap';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import type { FormDataType } from '@answer/common/interface';
|
||||
import { noticeSet, getUserInfo } from '@answer/api';
|
||||
import { setNotice, getUserInfo } from '@answer/api';
|
||||
import { useToast } from '@answer/hooks';
|
||||
|
||||
const Index = () => {
|
||||
|
@ -34,7 +34,7 @@ const Index = () => {
|
|||
const handleSubmit = (event: FormEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
noticeSet({
|
||||
setNotice({
|
||||
notice_switch: formData.notice_switch.value,
|
||||
}).then(() => {
|
||||
toast.onShow({
|
||||
|
|
|
@ -9,7 +9,7 @@ const Suspended = () => {
|
|||
const userInfo = userInfoStore((state) => state.user);
|
||||
|
||||
if (userInfo.status !== 'forbidden') {
|
||||
window.location.href = '/';
|
||||
window.location.replace('/');
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export const readNotification = (id) => {
|
|||
});
|
||||
};
|
||||
|
||||
export const useQueryNotificationRedDot = () => {
|
||||
export const useQueryNotificationStatus = () => {
|
||||
const apiUrl = '/answer/api/v1/notification/status';
|
||||
|
||||
return useSWR<{ inbox: number; achievement: number }>(
|
||||
|
@ -41,13 +41,13 @@ export const useQueryNotificationRedDot = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export const clearNotificationRedDot = (type) => {
|
||||
export const clearNotificationStatus = (type) => {
|
||||
return request.instance.put('/answer/api/v1/notification/status', {
|
||||
type,
|
||||
});
|
||||
};
|
||||
|
||||
export const clearUnReadNotification = (type) => {
|
||||
export const clearUnreadNotification = (type) => {
|
||||
return request.instance.put('/answer/api/v1/notification/read/state/all', {
|
||||
type,
|
||||
});
|
||||
|
|
|
@ -96,11 +96,11 @@ export const logout = () => {
|
|||
return request.get('/answer/api/v1/user/logout');
|
||||
};
|
||||
|
||||
export const emailVerify = (code: string) => {
|
||||
export const verifyEmail = (code: string) => {
|
||||
return request.get(`/answer/api/v1/email/verify?code=${code}`);
|
||||
};
|
||||
|
||||
export const emailReSend = (params?: Type.ImgCodeReq) => {
|
||||
export const resendEmail = (params?: Type.ImgCodeReq) => {
|
||||
params = qs.parse(
|
||||
qs.stringify(params, {
|
||||
skipNulls: true,
|
||||
|
@ -119,7 +119,7 @@ export const getUserInfo = () => {
|
|||
return request.get<Type.UserInfoRes>('/answer/api/v1/user/info');
|
||||
};
|
||||
|
||||
export const modifyPassword = (params: Type.ModifyPassReq) => {
|
||||
export const modifyPassword = (params: Type.ModifyPasswordReq) => {
|
||||
return request.post('/answer/api/v1/user/password/modify', params);
|
||||
};
|
||||
|
||||
|
@ -131,15 +131,15 @@ export const uploadAvatar = (params: Type.AvatarUploadReq) => {
|
|||
return request.post('/answer/api/v1/user/avatar/upload', params);
|
||||
};
|
||||
|
||||
export const passRetrieve = (params: Type.PssRetReq) => {
|
||||
export const resetPassword = (params: Type.PasswordResetReq) => {
|
||||
return request.post('/answer/api/v1/user/password/reset', params);
|
||||
};
|
||||
|
||||
export const passRetrieveSet = (params: { code: string; pass: string }) => {
|
||||
export const replacementPassword = (params: { code: string; pass: string }) => {
|
||||
return request.post('/answer/api/v1/user/password/replacement', params);
|
||||
};
|
||||
|
||||
export const accountActivate = (code: string) => {
|
||||
export const activateAccount = (code: string) => {
|
||||
return request.post(`/answer/api/v1/user/email/verification`, { code });
|
||||
};
|
||||
|
||||
|
@ -149,7 +149,7 @@ export const checkImgCode = (params: Type.CheckImgReq) => {
|
|||
);
|
||||
};
|
||||
|
||||
export const noticeSet = (params: Type.NoticeSetReq) => {
|
||||
export const setNotice = (params: Type.SetNoticeReq) => {
|
||||
return request.post('/answer/api/v1/user/notice/set', params);
|
||||
};
|
||||
|
||||
|
@ -158,7 +158,9 @@ export const saveQuestion = (params: Type.QuestionParams) => {
|
|||
};
|
||||
|
||||
export const questionDetail = (id: string) => {
|
||||
return request.get<Type.QuDetailRes>(`/answer/api/v1/question/info?id=${id}`);
|
||||
return request.get<Type.QuestionDetailRes>(
|
||||
`/answer/api/v1/question/info?id=${id}`,
|
||||
);
|
||||
};
|
||||
|
||||
export const langConfig = () => {
|
||||
|
@ -227,11 +229,11 @@ export const postReport = (params: {
|
|||
return request.post('/answer/api/v1/report', params);
|
||||
};
|
||||
|
||||
export const questionDelete = (params: { id: string }) => {
|
||||
export const deleteQuestion = (params: { id: string }) => {
|
||||
return request.delete('/answer/api/v1/question', params);
|
||||
};
|
||||
|
||||
export const answerDelete = (params: { id: string }) => {
|
||||
export const deleteAnswer = (params: { id: string }) => {
|
||||
return request.delete('/answer/api/v1/answer', params);
|
||||
};
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ function isLogin(needToLogin?: boolean): boolean {
|
|||
// login and active
|
||||
if (user.username && user.mail_status === 1) {
|
||||
if (LOGIN_NEED_BACK.includes(path)) {
|
||||
window.location.href = '/';
|
||||
window.location.replace('/');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -6,12 +6,6 @@ import { userInfoStore, toastStore } from '@answer/stores';
|
|||
|
||||
import Storage from './storage';
|
||||
|
||||
// type Result<T> = {
|
||||
// code: number;
|
||||
// msg: string;
|
||||
// data: T;
|
||||
// };
|
||||
|
||||
const API = {
|
||||
development: '',
|
||||
production: '',
|
||||
|
|
Loading…
Reference in New Issue