diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml index afe6c201..1abe4db0 100644 --- a/i18n/en_US.yaml +++ b/i18n/en_US.yaml @@ -1161,6 +1161,7 @@ ui: tag: Tag post_lowercase: post filter: Filter + ignore: Ignore search: title: Search Results keywords: Keywords @@ -1800,8 +1801,9 @@ ui: edit_answer: Edit answer edit_tag: Edit tag empty: No review tasks left. - approve_tip: Do you approve this revision? + approve_this_type: Do you approve this {{ type }}? suggest_type_edit: Suggest {{ type }} edit + flag_type: Flag {{ type }} filter_label: Type queued_post: Queued post queued_user: Queued user @@ -1809,6 +1811,7 @@ ui: flagged_user: Flagged user suggested_post_edit: Suggested post edit suggested_tag_edit: Suggested tag edit + reputation: reputation timeline: undeleted: undeleted deleted: deleted diff --git a/ui/src/pages/Review/components/ApproveDropdown/index.tsx b/ui/src/pages/Review/components/ApproveDropdown/index.tsx new file mode 100644 index 00000000..7f0e6bd7 --- /dev/null +++ b/ui/src/pages/Review/components/ApproveDropdown/index.tsx @@ -0,0 +1,37 @@ +import { useState } from 'react'; +import { Dropdown, Button } from 'react-bootstrap'; + +import EditPostModal from '../EditPostModal'; + +const Index = () => { + const [showEditPostModal, setShowEditPostModal] = useState(false); + + const handleEditPostModalState = () => { + setShowEditPostModal(!showEditPostModal); + }; + + return ( +
+ + + Approve + + + + Deactivate user + Suspend user + Delete user + + + +
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Review/components/EditPostModal/index.scss b/ui/src/pages/Review/components/EditPostModal/index.scss new file mode 100644 index 00000000..550155b2 --- /dev/null +++ b/ui/src/pages/Review/components/EditPostModal/index.scss @@ -0,0 +1,4 @@ +.edit-post-modal { + max-width: 746px; + width: 58.3333% !important; +} diff --git a/ui/src/pages/Review/components/EditPostModal/index.tsx b/ui/src/pages/Review/components/EditPostModal/index.tsx new file mode 100644 index 00000000..a07a70cf --- /dev/null +++ b/ui/src/pages/Review/components/EditPostModal/index.tsx @@ -0,0 +1,195 @@ +import { FC, useState } from 'react'; +import { Modal, Button, Form } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; + +import classNames from 'classnames'; + +import { modifyQuestion } from '@/services'; +import { useCaptchaModal } from '@/hooks'; +import { Editor, TagSelector } from '@/components'; +import { handleFormError } from '@/utils'; +import type * as Type from '@/common/interface'; + +import './index.scss'; + +interface Props { + visible: boolean; + handleClose: () => void; +} + +interface FormDataItem { + title: Type.FormValue; + tags: Type.FormValue; + content: Type.FormValue; +} + +const Index: FC = ({ visible = false, handleClose }) => { + const initFormData = { + title: { + value: '', + isInvalid: false, + errorMsg: '', + }, + tags: { + value: [], + isInvalid: false, + errorMsg: '', + }, + content: { + value: '', + isInvalid: false, + errorMsg: '', + }, + }; + const { t } = useTranslation('translation', { keyPrefix: 'ask' }); + const [formData, setFormData] = useState(initFormData); + const [focusEditor, setFocusEditor] = useState(false); + + const editCaptcha = useCaptchaModal('edit'); + + const handleInput = (data: Partial) => { + setFormData({ + ...formData, + ...data, + }); + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + event.stopPropagation(); + + const params: Type.QuestionParams = { + title: formData.title.value, + content: formData.content.value, + tags: formData.tags.value, + }; + + editCaptcha.check(() => { + const ep = { + ...params, + id: '', + edit_summary: '', + }; + const imgCode = editCaptcha.getCaptcha(); + if (imgCode.verify) { + ep.captcha_code = imgCode.captcha_code; + ep.captcha_id = imgCode.captcha_id; + } + modifyQuestion(ep) + .then(async (res) => { + await editCaptcha.close(); + console.log('res', res); + // navigate(pathFactory.questionLanding(qid, res?.url_title), { + // state: { isReview: res?.wait_for_review }, + // }); + }) + .catch((err) => { + if (err.isError) { + editCaptcha.handleCaptchaError(err.list); + const data = handleFormError(err, formData); + setFormData({ ...data }); + } + }); + }); + }; + return ( + + + Edit post + + +
+ + {t('form.fields.title.label')} + { + handleInput({ + title: { + value: e.target.value, + isInvalid: false, + errorMsg: '', + }, + }); + }} + placeholder={t('form.fields.title.placeholder')} + autoFocus + contentEditable + /> + + + {formData.title.errorMsg} + + + + + {t('form.fields.body.label')} + + + {t('form.fields.tags.label')} + +
+
+ + + + +
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Review/components/Filter/index.tsx b/ui/src/pages/Review/components/Filter/index.tsx new file mode 100644 index 00000000..2eccf86a --- /dev/null +++ b/ui/src/pages/Review/components/Filter/index.tsx @@ -0,0 +1,33 @@ +import { useState } from 'react'; +import { Card, Form } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; + +const Index = () => { + const { t } = useTranslation('translation', { keyPrefix: 'page_review' }); + const [checked, setValue] = useState(false); + return ( + + {t('filter', { keyPrefix: 'btns' })} + + + {t('filter_label')} + setValue(e.target.checked)} + /> + + setValue(e.target.checked)} + /> + + + + ); +}; + +export default Index; diff --git a/ui/src/pages/Review/components/FlagContent/index.tsx b/ui/src/pages/Review/components/FlagContent/index.tsx new file mode 100644 index 00000000..fdead753 --- /dev/null +++ b/ui/src/pages/Review/components/FlagContent/index.tsx @@ -0,0 +1,103 @@ +import { FC } from 'react'; +import { Card, Badge } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; + +import { BaseUserCard, Tag, FormatTime, Avatar } from '@/components'; + +const tag = [ + { + display_name: 'bug', + slug_name: 'bug', + original_text: '111', + recommend: true, + }, + { + display_name: 'react', + slug_name: 'react', + original_text: '222', + reserved: true, + }, + { + display_name: 'test', + slug_name: 'test', + original_text: '111', + recommend: false, + reserved: false, + }, +]; + +const Index: FC = () => { + const { t } = useTranslation('translation', { keyPrefix: 'page_review' }); + const objectType = 'question'; + return ( + + {t('flag_type', { type: 'post' })} + +
+
+ How do I test weather variable against multiple +
+ {objectType === 'question' && ( +
+ {tag?.map((item) => { + return ( + + ); + })} +
+ )} +
+ Python is a multi-paradigm, dynamically typed, multi-purpose + programming language. It is designed to be quick to learn, + understand, and use, and enforces a clean and uniform syntax. Please + note that Python 2 is officially out of support as of 2020-01-01. + For version-specific Python questions, add the [python-2.7] or + [python-3.x] tag. When using a Python variant library (e.g. Pandas, + NumPy), please include it in the tags. +
+
+ normal +
+ + +
+
+
+ +
+ +
+ + 111 @111 + +
+ I'm a web developer with in-depth experience in UI/UX design. +
+
280 {t('reputation')}
+
+
+
+
+ ); +}; + +export default Index; diff --git a/ui/src/pages/Review/components/index.ts b/ui/src/pages/Review/components/index.ts new file mode 100644 index 00000000..74f4f1df --- /dev/null +++ b/ui/src/pages/Review/components/index.ts @@ -0,0 +1,6 @@ +import Filter from './Filter'; +import ApproveDropdown from './ApproveDropdown'; +import EditPostModal from './EditPostModal'; +import FlagContent from './FlagContent'; + +export { Filter, ApproveDropdown, EditPostModal, FlagContent }; diff --git a/ui/src/pages/Review/index.tsx b/ui/src/pages/Review/index.tsx index 6f0632c6..77af95b9 100644 --- a/ui/src/pages/Review/index.tsx +++ b/ui/src/pages/Review/index.tsx @@ -18,7 +18,7 @@ */ import { FC, useEffect, useState } from 'react'; -import { Row, Col, Alert, Stack, Button, Card, Form } from 'react-bootstrap'; +import { Row, Col, Alert, Stack, Button, Card } from 'react-bootstrap'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -29,6 +29,8 @@ import { pathFactory } from '@/router/pathFactory'; import { scrollToDocTop } from '@/utils'; import type * as Type from '@/common/interface'; +import { Filter, ApproveDropdown, FlagContent } from './components'; + const Index: FC = () => { const { t } = useTranslation('translation', { keyPrefix: 'page_review' }); const [isLoading, setIsLoading] = useState(false); @@ -107,7 +109,6 @@ const Index: FC = () => { setIsLoading(false); }); }; - let itemLink = ''; let itemId = ''; let editSummary = unreviewed_info?.reason; @@ -138,6 +139,47 @@ const Index: FC = () => { usePageTags({ title: t('review'), }); + + let newData: Record = {}; + let oldData: Record = {}; + let diffOpts: Partial<{ + showTitle: boolean; + showTagUrlSlug: boolean; + }> = { + showTitle: true, + showTagUrlSlug: true, + }; + if (type === 'question' && info && reviewInfo && 'content' in reviewInfo) { + newData = { + title: reviewInfo.title, + original_text: reviewInfo.content, + tags: reviewInfo.tags, + }; + oldData = { + title: info.title, + original_text: info.content, + tags: info.tags, + }; + } + if (type === 'answer' && info && reviewInfo && 'content' in reviewInfo) { + newData = { + original_text: reviewInfo.content, + }; + oldData = { + original_text: info.content, + }; + } + + if (type === 'tag' && info && reviewInfo) { + newData = { + original_text: reviewInfo.original_text, + }; + oldData = { + original_text: info.content, + }; + diffOpts = { showTitle: false, showTagUrlSlug: false }; + } + return (

{t('review')}

@@ -181,58 +223,18 @@ const Index: FC = () => { #{itemId} - {type === 'question' && - info && - reviewInfo && - 'content' in reviewInfo && ( - - )} - {type === 'answer' && - info && - reviewInfo && - 'content' in reviewInfo && ( - - )} - {type === 'tag' && info && reviewInfo && ( - - )} + -

{t('approve_tip')}

+

{t('approve_this_type', { type: 'revision' })}

+ +