mirror of https://gitee.com/answerdev/answer.git
feat(review): ready for testing
This commit is contained in:
parent
81ec786366
commit
079513e8b6
|
@ -1190,6 +1190,9 @@ ui:
|
|||
answer_edit: Answer edit
|
||||
tag_edit: Tag edit
|
||||
edit_summary: Edit summary
|
||||
edit_question: Edit question
|
||||
edit_answer: Edit answer
|
||||
edit_tag: Edit tag
|
||||
empty: No review tasks left.
|
||||
timeline:
|
||||
undeleted: undeleted
|
||||
|
|
|
@ -157,6 +157,13 @@ export interface SetNoticeReq {
|
|||
notice_switch: boolean;
|
||||
}
|
||||
|
||||
export interface NotificationStatus {
|
||||
inbox: number;
|
||||
achievement: number;
|
||||
revision: number;
|
||||
can_revision: boolean;
|
||||
}
|
||||
|
||||
export interface QuestionDetailRes {
|
||||
id: string;
|
||||
title: string;
|
||||
|
@ -192,7 +199,6 @@ export interface AnswerItem {
|
|||
create_time: string;
|
||||
update_time: string;
|
||||
user_info: UserInfoBase;
|
||||
|
||||
[prop: string]: any;
|
||||
}
|
||||
|
||||
|
@ -406,3 +412,29 @@ export interface TimelineRes {
|
|||
object_info: TimelineObject;
|
||||
timeline: TimelineItem[];
|
||||
}
|
||||
|
||||
export interface ReviewItem {
|
||||
type: 'question' | 'answer' | 'tag';
|
||||
info: {
|
||||
object_id: string;
|
||||
title: string;
|
||||
content: string;
|
||||
html: string;
|
||||
tags: Tag[];
|
||||
};
|
||||
unreviewed_info: {
|
||||
id: string;
|
||||
use_id: string;
|
||||
object_id: string;
|
||||
title: string;
|
||||
status: 0 | 1;
|
||||
create_at: number;
|
||||
user_info: UserInfoBase;
|
||||
reason: string;
|
||||
content: Tag | QuestionDetailRes | AnswerItem;
|
||||
};
|
||||
}
|
||||
export interface ReviewResp {
|
||||
count: number;
|
||||
list: ReviewItem[];
|
||||
}
|
||||
|
|
|
@ -4,13 +4,26 @@ import { Tag } from '@/components';
|
|||
import { diffText } from '@/utils';
|
||||
|
||||
interface Props {
|
||||
objectType: string;
|
||||
objectType: string | 'question' | 'answer' | 'tag';
|
||||
newData: Record<string, any>;
|
||||
oldData?: Record<string, any>;
|
||||
className?: string;
|
||||
opts?: Partial<{
|
||||
showTitle: boolean;
|
||||
showTagUrlSlug: boolean;
|
||||
}>;
|
||||
}
|
||||
|
||||
const Index: FC<Props> = ({ objectType, newData, oldData, className = '' }) => {
|
||||
const Index: FC<Props> = ({
|
||||
objectType,
|
||||
newData,
|
||||
oldData,
|
||||
className = '',
|
||||
opts = {
|
||||
showTitle: true,
|
||||
showTagUrlSlug: true,
|
||||
},
|
||||
}) => {
|
||||
if (!newData?.original_text) return null;
|
||||
|
||||
let tag = newData.tags;
|
||||
|
@ -51,7 +64,7 @@ const Index: FC<Props> = ({ objectType, newData, oldData, className = '' }) => {
|
|||
|
||||
return (
|
||||
<div className={className}>
|
||||
{objectType !== 'answer' && (
|
||||
{objectType !== 'answer' && opts?.showTitle && (
|
||||
<h5
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: diffText(newData.title, oldData?.title),
|
||||
|
@ -73,7 +86,7 @@ const Index: FC<Props> = ({ objectType, newData, oldData, className = '' }) => {
|
|||
})}
|
||||
</div>
|
||||
)}
|
||||
{objectType === 'tag' && (
|
||||
{objectType === 'tag' && opts?.showTagUrlSlug && (
|
||||
<div className="mb-4">
|
||||
{`/tags/${
|
||||
newData?.main_tag_slug_name
|
||||
|
|
|
@ -3,10 +3,11 @@ import { Nav, Dropdown } from 'react-bootstrap';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
|
||||
import type * as Type from '@/common/interface';
|
||||
import { Avatar, Icon } from '@/components';
|
||||
|
||||
interface Props {
|
||||
redDot;
|
||||
redDot: Type.NotificationStatus | undefined;
|
||||
userInfo;
|
||||
logOut: () => void;
|
||||
}
|
||||
|
@ -56,10 +57,14 @@ const Index: FC<Props> = ({ redDot, userInfo, logOut }) => {
|
|||
{userInfo?.is_admin ? (
|
||||
<Dropdown.Item href="/admin">{t('header.nav.admin')}</Dropdown.Item>
|
||||
) : null}
|
||||
{/* TODO: use review permission */}
|
||||
{userInfo?.is_admin ? (
|
||||
<Dropdown.Item href="/review">
|
||||
{redDot?.can_revision ? (
|
||||
<Dropdown.Item href="/review" className="position-relative">
|
||||
{t('header.nav.review')}
|
||||
{redDot?.revision > 0 && (
|
||||
<span className="position-absolute top-50 translate-middle-y end-0 me-3 p-2 bg-danger border border-light rounded-circle">
|
||||
<span className="visually-hidden">New Review</span>
|
||||
</span>
|
||||
)}
|
||||
</Dropdown.Item>
|
||||
) : null}
|
||||
<Dropdown.Divider />
|
||||
|
|
|
@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { Modal } from '@/components';
|
||||
import { useReportModal, useToast } from '@/hooks';
|
||||
import Share from '../Share';
|
||||
import { deleteQuestion, deleteAnswer } from '@/services';
|
||||
import { deleteQuestion, deleteAnswer, editCheck } from '@/services';
|
||||
import { tryNormalLogged } from '@/utils/guard';
|
||||
|
||||
interface IProps {
|
||||
|
@ -96,6 +96,15 @@ const Index: FC<IProps> = ({
|
|||
});
|
||||
}
|
||||
};
|
||||
const handleEdit = async (evt) => {
|
||||
let checkObjectId = qid;
|
||||
if (type === 'answer') {
|
||||
checkObjectId = aid;
|
||||
}
|
||||
editCheck(checkObjectId).catch(() => {
|
||||
evt.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
const handleAction = (action) => {
|
||||
if (!tryNormalLogged(true)) {
|
||||
|
@ -124,6 +133,7 @@ const Index: FC<IProps> = ({
|
|||
key={item.action}
|
||||
to={editUrl}
|
||||
className="link-secondary p-0 fs-14 me-3"
|
||||
onClick={handleEdit}
|
||||
style={{ lineHeight: '23px' }}>
|
||||
{item.name}
|
||||
</Link>
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, { memo, FC } from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { Tag } from '@/common/interface';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
interface IProps {
|
||||
data: Tag;
|
||||
|
@ -17,9 +18,7 @@ const Index: FC<IProps> = ({
|
|||
className = '',
|
||||
textClassName = '',
|
||||
}) => {
|
||||
href ||= `/tags/${encodeURIComponent(
|
||||
data.main_tag_slug_name || data.slug_name,
|
||||
)}`.toLowerCase();
|
||||
href ||= pathFactory.tagLanding(data);
|
||||
|
||||
return (
|
||||
<a
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FC } from 'react';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
|
@ -11,61 +11,218 @@ import {
|
|||
import { Link } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { BaseUserCard, FormatTime, Empty } from '@/components';
|
||||
import { loggedUserInfoStore } from '@/stores';
|
||||
import { BaseUserCard, FormatTime, Empty, DiffContent } from '@/components';
|
||||
import { useReviewList, revisionAudit } from '@/services';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import type * as Type from '@/common/interface';
|
||||
|
||||
const Index: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'page_review' });
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [noTasks, setNoTasks] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const { data: reviewResp, mutate: mutateList } = useReviewList(page);
|
||||
const ro = reviewResp?.list[0];
|
||||
const { info, type, unreviewed_info } = ro || {
|
||||
info: null,
|
||||
type: '',
|
||||
unreviewed_info: null,
|
||||
};
|
||||
const reviewInfo = unreviewed_info?.content;
|
||||
const mutateNextPage = () => {
|
||||
const count = reviewResp?.count;
|
||||
if (count && page < count) {
|
||||
setPage(page + 1);
|
||||
} else {
|
||||
setNoTasks(true);
|
||||
}
|
||||
};
|
||||
const handlingSkip = () => {
|
||||
mutateNextPage();
|
||||
};
|
||||
const handlingApprove = () => {
|
||||
if (!unreviewed_info) {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
revisionAudit(unreviewed_info.id, 'approve')
|
||||
.then(() => {
|
||||
mutateList();
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log('ex: ', ex);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
const handlingReject = () => {
|
||||
if (!unreviewed_info) {
|
||||
return;
|
||||
}
|
||||
setIsLoading(true);
|
||||
revisionAudit(unreviewed_info.id, 'reject')
|
||||
.then(() => {
|
||||
mutateList();
|
||||
})
|
||||
.catch((ex) => {
|
||||
console.log('ex: ', ex);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const { user } = loggedUserInfoStore.getState();
|
||||
let itemLink = '';
|
||||
let itemTitle = '';
|
||||
let editBadge = '';
|
||||
let editSummary = unreviewed_info?.reason;
|
||||
const editor = unreviewed_info?.user_info;
|
||||
const editTime = unreviewed_info?.create_at;
|
||||
if (type === 'question') {
|
||||
itemLink = pathFactory.questionLanding(info?.object_id);
|
||||
itemTitle = info?.title;
|
||||
editBadge = t('question_edit');
|
||||
editSummary ||= t('edit_question');
|
||||
} else if (type === 'answer') {
|
||||
itemLink = pathFactory.answerLanding(
|
||||
// @ts-ignore
|
||||
unreviewed_info.content.question_id,
|
||||
unreviewed_info.object_id,
|
||||
);
|
||||
itemTitle = info?.title;
|
||||
editBadge = t('answer_edit');
|
||||
editSummary ||= t('edit_answer');
|
||||
} else if (type === 'tag') {
|
||||
const tagInfo = unreviewed_info.content as Type.Tag;
|
||||
itemLink = pathFactory.tagLanding(tagInfo);
|
||||
itemTitle = tagInfo.display_name;
|
||||
editBadge = t('tag_edit');
|
||||
editSummary ||= t('edit_tag');
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!reviewResp) {
|
||||
return;
|
||||
}
|
||||
window.scrollTo({ top: 0 });
|
||||
if (!reviewResp.list || !reviewResp.list.length) {
|
||||
setNoTasks(true);
|
||||
}
|
||||
}, [reviewResp]);
|
||||
return (
|
||||
<Container className="pt-2 mt-4 mb-5">
|
||||
<Row>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<h3 className="mb-4">{t('review')}</h3>
|
||||
<Alert variant="secondary">
|
||||
<Stack className="align-items-start">
|
||||
<Badge bg="secondary" className="mb-2">
|
||||
{t('question_edit')}
|
||||
</Badge>
|
||||
<Link to="/review">
|
||||
How do I test whether variable against multiple
|
||||
</Link>
|
||||
<p className="mb-0">
|
||||
{t('edit_summary')}: Editing part of the code and correcting the
|
||||
grammar.
|
||||
</p>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
gap={1}
|
||||
className="align-items-baseline mt-2">
|
||||
<BaseUserCard data={user} avatarSize="24" />
|
||||
<FormatTime
|
||||
time={Date.now()}
|
||||
className="small text-secondary"
|
||||
preFix={t('proposed')}
|
||||
/>
|
||||
</Stack>
|
||||
</Alert>
|
||||
</Col>
|
||||
<Col lg={{ span: 7, offset: 1 }}>Content</Col>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Stack direction="horizontal" gap={2}>
|
||||
<Button variant="outline-primary">
|
||||
{t('approve', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
<Button variant="outline-primary">
|
||||
{t('reject', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
<Button variant="outline-primary">
|
||||
{t('skip', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Col>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Empty>{t('empty')}</Empty>
|
||||
</Col>
|
||||
{!noTasks && ro && (
|
||||
<>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Alert variant="secondary">
|
||||
<Stack className="align-items-start">
|
||||
<Badge bg="secondary" className="mb-2">
|
||||
{editBadge}
|
||||
</Badge>
|
||||
<Link to={itemLink} target="_blank">
|
||||
{itemTitle}
|
||||
</Link>
|
||||
<p className="mb-0">
|
||||
{t('edit_summary')}: {editSummary}
|
||||
</p>
|
||||
</Stack>
|
||||
<Stack
|
||||
direction="horizontal"
|
||||
gap={1}
|
||||
className="align-items-baseline mt-2">
|
||||
<BaseUserCard data={editor} avatarSize="24" />
|
||||
{editTime && (
|
||||
<FormatTime
|
||||
time={editTime}
|
||||
className="small text-secondary"
|
||||
preFix={t('proposed')}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
</Alert>
|
||||
</Col>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
{type === 'question' &&
|
||||
info &&
|
||||
reviewInfo &&
|
||||
'content' in reviewInfo && (
|
||||
<DiffContent
|
||||
className="mt-2"
|
||||
objectType={type}
|
||||
oldData={{
|
||||
title: info.title,
|
||||
original_text: info.content,
|
||||
tags: info.tags,
|
||||
}}
|
||||
newData={{
|
||||
title: reviewInfo.title,
|
||||
original_text: reviewInfo.content,
|
||||
tags: reviewInfo.tags,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{type === 'answer' &&
|
||||
info &&
|
||||
reviewInfo &&
|
||||
'content' in reviewInfo && (
|
||||
<DiffContent
|
||||
className="mt-2"
|
||||
objectType={type}
|
||||
newData={{
|
||||
original_text: reviewInfo.content,
|
||||
}}
|
||||
oldData={{
|
||||
original_text: info.content,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{type === 'tag' && info && reviewInfo && (
|
||||
<DiffContent
|
||||
className="mt-2"
|
||||
objectType={type}
|
||||
newData={{
|
||||
original_text: reviewInfo.original_text,
|
||||
}}
|
||||
oldData={{
|
||||
original_text: info.content,
|
||||
}}
|
||||
opts={{ showTitle: false, showTagUrlSlug: false }}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Stack direction="horizontal" gap={2} className="mt-4">
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
disabled={isLoading}
|
||||
onClick={handlingApprove}>
|
||||
{t('approve', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
disabled={isLoading}
|
||||
onClick={handlingReject}>
|
||||
{t('reject', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline-primary"
|
||||
disabled={isLoading}
|
||||
onClick={handlingSkip}>
|
||||
{t('skip', { keyPrefix: 'btns' })}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Col>
|
||||
</>
|
||||
)}
|
||||
{noTasks && (
|
||||
<Col lg={{ span: 7, offset: 1 }}>
|
||||
<Empty>{t('empty')}</Empty>
|
||||
</Col>
|
||||
)}
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { following } from '@/services';
|
||||
import { tryNormalLogged } from '@/utils/guard';
|
||||
import { escapeRemove } from '@/utils';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
interface Props {
|
||||
data;
|
||||
|
@ -53,7 +54,9 @@ const Index: FC<Props> = ({ data }) => {
|
|||
{data.excerpt && (
|
||||
<p className="text-break">
|
||||
{escapeRemove(data.excerpt)}
|
||||
<Link to={`/tags/${data.slug_name}/info`}> [{t('more')}]</Link>
|
||||
<Link className="ms-1" to={pathFactory.tagInfo(data.slug_name)}>
|
||||
[{t('more')}]
|
||||
</Link>
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import { useTagInfo, useFollow } from '@/services';
|
|||
import QuestionList from '@/components/QuestionList';
|
||||
import HotQuestions from '@/components/HotQuestions';
|
||||
import { escapeRemove } from '@/utils';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
|
||||
const Questions: FC = () => {
|
||||
const { t } = useTranslation('translation', { keyPrefix: 'tags' });
|
||||
|
@ -30,7 +31,7 @@ const Questions: FC = () => {
|
|||
if (tagResp) {
|
||||
const info = { ...tagResp };
|
||||
if (info.main_tag_slug_name) {
|
||||
navigate(`/tags/${info.main_tag_slug_name}`, { replace: true });
|
||||
navigate(pathFactory.tagLanding(info), { replace: true });
|
||||
return;
|
||||
}
|
||||
if (followResp) {
|
||||
|
@ -62,7 +63,7 @@ const Questions: FC = () => {
|
|||
<div className="tag-box mb-5">
|
||||
<h3 className="mb-3">
|
||||
<Link
|
||||
to={`/tags/${tagInfo?.slug_name}`}
|
||||
to={pathFactory.tagLanding(tagInfo)}
|
||||
replace
|
||||
className="link-dark">
|
||||
{tagInfo.display_name}
|
||||
|
@ -71,9 +72,7 @@ const Questions: FC = () => {
|
|||
|
||||
<p className="text-break">
|
||||
{escapeRemove(tagInfo.excerpt) || t('no_description')}
|
||||
<Link
|
||||
to={`/tags/${encodeURIComponent(curTagName)}/info`}
|
||||
className="ms-1">
|
||||
<Link to={pathFactory.tagInfo(curTagName)} className="ms-1">
|
||||
[{t('more')}]
|
||||
</Link>
|
||||
</p>
|
||||
|
|
|
@ -175,7 +175,7 @@ const Ask = () => {
|
|||
<Form.Control
|
||||
value={formData.displayName.value}
|
||||
isInvalid={formData.displayName.isInvalid}
|
||||
disabled={is_admin}
|
||||
disabled={!is_admin}
|
||||
onChange={handleDisplayNameChange}
|
||||
/>
|
||||
|
||||
|
@ -188,7 +188,7 @@ const Ask = () => {
|
|||
<Form.Control
|
||||
value={formData.slugName.value}
|
||||
isInvalid={formData.slugName.isInvalid}
|
||||
disabled={is_admin}
|
||||
disabled={!is_admin}
|
||||
onChange={handleSlugNameChange}
|
||||
/>
|
||||
<Form.Text as="div">
|
||||
|
|
|
@ -11,7 +11,9 @@ import {
|
|||
useQuerySynonymsTags,
|
||||
saveSynonymsTags,
|
||||
deleteTag,
|
||||
editCheck,
|
||||
} from '@/services';
|
||||
import { pathFactory } from '@/router/pathFactory';
|
||||
import { loggedUserInfoStore } from '@/stores';
|
||||
|
||||
const TagIntroduction = () => {
|
||||
|
@ -27,7 +29,9 @@ const TagIntroduction = () => {
|
|||
return null;
|
||||
}
|
||||
if (tagInfo.main_tag_slug_name) {
|
||||
navigate(`/tags/${tagInfo.main_tag_slug_name}/info`, { replace: true });
|
||||
navigate(pathFactory.tagInfo(tagInfo.main_tag_slug_name), {
|
||||
replace: true,
|
||||
});
|
||||
return null;
|
||||
}
|
||||
const handleEdit = () => {
|
||||
|
@ -51,7 +55,9 @@ const TagIntroduction = () => {
|
|||
};
|
||||
|
||||
const handleEditTag = () => {
|
||||
navigate(`/tags/${tagInfo?.tag_id}/edit`);
|
||||
editCheck(tagInfo?.tag_id).then(() => {
|
||||
navigate(pathFactory.tagEdit(tagInfo?.tag_id));
|
||||
});
|
||||
};
|
||||
const handleDeleteTag = () => {
|
||||
if (synonymsTags && synonymsTags.length > 0) {
|
||||
|
@ -93,7 +99,7 @@ const TagIntroduction = () => {
|
|||
<Col xxl={7} lg={8} sm={12}>
|
||||
<h3 className="mb-3">
|
||||
<Link
|
||||
to={`/tags/${tagInfo?.slug_name}`}
|
||||
to={pathFactory.tagLanding(tagInfo)}
|
||||
replace
|
||||
className="link-dark">
|
||||
{tagInfo.display_name}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import type * as Type from '@/common/interface';
|
||||
|
||||
const tagLanding = (tag: Type.Tag) => {
|
||||
let slugName = tag.main_tag_slug_name || tag.slug_name || '';
|
||||
slugName = slugName.toLowerCase();
|
||||
return `/tags/${encodeURIComponent(slugName)}`;
|
||||
};
|
||||
const tagInfo = (slugName: string) => {
|
||||
slugName = slugName.toLowerCase();
|
||||
return `/tags/${encodeURIComponent(slugName)}/info`;
|
||||
};
|
||||
const tagEdit = (tagId: string) => {
|
||||
return `/tags/${tagId}/edit`;
|
||||
};
|
||||
const questionLanding = (question_id: string) => {
|
||||
return `/questions/${question_id}`;
|
||||
};
|
||||
const answerLanding = (question_id: string, answer_id: string) => {
|
||||
return `/questions/${question_id}/${answer_id}`;
|
||||
};
|
||||
|
||||
export const pathFactory = {
|
||||
tagLanding,
|
||||
tagInfo,
|
||||
tagEdit,
|
||||
questionLanding,
|
||||
answerLanding,
|
||||
};
|
|
@ -192,7 +192,6 @@ const routes: RouteNode[] = [
|
|||
{
|
||||
path: '/users/confirm-new-email',
|
||||
page: 'pages/Users/ConfirmNewEmail',
|
||||
// TODO: guard this
|
||||
},
|
||||
{
|
||||
path: '/users/account-suspended',
|
||||
|
|
|
@ -7,3 +7,4 @@ export * from './tag';
|
|||
export * from './settings';
|
||||
export * from './legal';
|
||||
export * from './timeline';
|
||||
export * from './revision';
|
||||
|
|
|
@ -32,7 +32,7 @@ export const readNotification = (id) => {
|
|||
export const useQueryNotificationStatus = () => {
|
||||
const apiUrl = '/answer/api/v1/notification/status';
|
||||
|
||||
return useSWR<{ inbox: number; achievement: number }>(
|
||||
return useSWR<Type.NotificationStatus>(
|
||||
tryLoggedAndActivated().ok ? apiUrl : null,
|
||||
request.instance.get,
|
||||
{
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import useSWR from 'swr';
|
||||
|
||||
import request from '@/utils/request';
|
||||
import * as Type from '@/common/interface';
|
||||
|
||||
export const editCheck = (id: string) => {
|
||||
const apiUrl = `/answer/api/v1/revisions/edit/check?id=${id}`;
|
||||
return request.get(apiUrl);
|
||||
};
|
||||
|
||||
export const revisionAudit = (id: string, operation: 'approve' | 'reject') => {
|
||||
const apiUrl = `/answer/api/v1/revisions/audit`;
|
||||
return request.put(apiUrl, {
|
||||
id,
|
||||
operation,
|
||||
});
|
||||
};
|
||||
|
||||
export const useReviewList = (page: number) => {
|
||||
const apiUrl = `/answer/api/v1/revisions/unreviewed?page=${page}`;
|
||||
const { data, error, mutate } = useSWR<Type.ReviewResp, Error>(
|
||||
apiUrl,
|
||||
request.instance.get,
|
||||
);
|
||||
return {
|
||||
data,
|
||||
isLoading: !data && !error,
|
||||
error,
|
||||
mutate,
|
||||
};
|
||||
};
|
Loading…
Reference in New Issue