This is an automated email from the ASF dual-hosted git repository.
shuai pushed a commit to branch test
in repository https://gitbox.apache.org/repos/asf/incubator-answer.git
The following commit(s) were added to refs/heads/test by this push:
new 175c042a Feat/1.2.1/UI (#623)
175c042a is described below
commit 175c042af0f747b2e832fd816a2c7e0632d7c086
Author: dashuai <[email protected]>
AuthorDate: Wed Nov 15 12:00:07 2023 +0800
Feat/1.2.1/UI (#623)
---
i18n/de_DE.yaml | 6 ++---
i18n/en_US.yaml | 28 ++++++++++++++++-----
ui/src/common/interface.ts | 7 +++++-
ui/src/components/Comment/index.tsx | 20 +++++++++------
ui/src/components/Header/index.tsx | 2 +-
ui/src/components/PluginRender/index.tsx | 5 ----
ui/src/components/SchemaForm/components/Check.tsx | 1 -
ui/src/components/Tag/index.tsx | 2 +-
.../Dashboard/components/AnswerLinks/index.tsx | 29 ++++++++++++++++++++++
.../Dashboard/components/HealthStatus/index.tsx | 24 ++++++++++--------
.../Dashboard/components/Statistics/index.tsx | 2 +-
.../Dashboard/components/SystemInfo/index.tsx | 12 +++++++++
ui/src/pages/Admin/Login/index.tsx | 13 ++++++++++
ui/src/pages/Questions/Ask/index.tsx | 22 +++++++++++-----
ui/src/pages/Tags/Create/index.tsx | 2 +-
ui/src/pages/Tags/Edit/index.tsx | 2 +-
ui/src/pages/Timeline/index.tsx | 7 ++++--
ui/src/pages/Users/Login/index.tsx | 3 ++-
ui/src/router/pathFactory.ts | 4 +--
ui/src/services/common.ts | 7 ++----
ui/src/stores/loginSetting.ts | 1 +
ui/src/utils/request.ts | 3 +--
22 files changed, 145 insertions(+), 57 deletions(-)
diff --git a/i18n/de_DE.yaml b/i18n/de_DE.yaml
index 92e613da..a83b7dbc 100644
--- a/i18n/de_DE.yaml
+++ b/i18n/de_DE.yaml
@@ -722,9 +722,9 @@ ui:
btn_save_edits: Änderungen speichern
btn_cancel: Stornieren
dates:
- long_date: MMM D
- long_date_with_year: "MMM D, JJJJ"
- long_date_with_time: "MMM T, JJJJ [at] HH:mm"
+ long_date: DD. MMM
+ long_date_with_year: "DD. MMM YYYY"
+ long_date_with_time: "DD. MMM YYYY [at] HH:mm"
now: jetzt
x_seconds_ago: "vor {{count}}s"
x_minutes_ago: "vor {{count}}m"
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 2f32597c..368c85cf 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -1400,23 +1400,33 @@ ui:
dashboard:
title: Dashboard
welcome: Welcome to Answer Admin!
- site_statistics: Site Statistics
+ site_statistics: Site statistics
questions: "Questions:"
answers: "Answers:"
comments: "Comments:"
votes: "Votes:"
- active_users: "Active users:"
+ users: "Users:"
flags: "Flags:"
- site_health_status: Site Health Status
+ site_health_status: Site health status
version: "Version:"
https: "HTTPS:"
- uploading_files: "Uploading files:"
+ upload_folder: "Upload folder:"
+ run_mode: "Running mode:"
+ private: Private
+ public: Public
smtp: "SMTP:"
timezone: "Timezone:"
- system_info: System Info
+ system_info: System info
+ go_version: "Go version:"
+ database: "Database:"
+ database_size: "Database size:"
storage_used: "Storage used:"
uptime: "Uptime:"
- answer_links: Answer Links
+ answer_links: Answer links
+ plugins: Plugins
+ github: GitHub
+ blog: Blog
+ contact: Contact
documents: Documents
feedback: Feedback
support: Support
@@ -1431,6 +1441,8 @@ ui:
allowed: Allowed
enabled: Enabled
disabled: Disabled
+ writable: Writable
+ not_writable: Not writable
flags:
title: Flags
pending: Pending
@@ -1704,6 +1716,10 @@ ui:
title: Private
label: Login required
text: Only logged in users can access this community.
+ password_login:
+ title: Password login
+ label: Allow email and password login
+ text: "WARNING: If turn off, you may be unable to log in if you have
not previously configured other login method."
installed_plugins:
title: Installed Plugins
plugin_link: Plugins extend and expand the functionality of Answer. You
may find plugins in the <1>Answer Plugin Repository</1>.
diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts
index 47987606..a148d5ab 100644
--- a/ui/src/common/interface.ts
+++ b/ui/src/common/interface.ts
@@ -454,6 +454,7 @@ export interface AdminSettingsLogin {
login_required: boolean;
allow_email_registrations: boolean;
allow_email_domains: string[];
+ allow_password_login: boolean;
}
/**
@@ -507,11 +508,15 @@ export interface AdminDashboard {
user_count: number;
report_count: number;
uploading_files: boolean;
- smtp: boolean;
+ smtp: 'enabled' | 'disabled' | 'not_configured';
time_zone: string;
occupying_storage_space: string;
app_start_time: number;
https: boolean;
+ login_required: boolean;
+ go_version: string;
+ database_version: string;
+ database_size: string;
version_info: {
remote_version: string;
version: string;
diff --git a/ui/src/components/Comment/index.tsx
b/ui/src/components/Comment/index.tsx
index e9118356..cf84be88 100644
--- a/ui/src/components/Comment/index.tsx
+++ b/ui/src/components/Comment/index.tsx
@@ -328,16 +328,17 @@ const Comment = ({ objectId, mode, commentId }) => {
);
};
return (
- <div className="comments-wrap">
- {comments.map((item, index) => {
+ <div
+ className={classNames(
+ 'comments-wrap',
+ comments.length > 0 && 'bg-light px-3 py-2 rounded',
+ )}>
+ {comments.map((item) => {
return (
<div
key={item.comment_id}
id={item.comment_id}
- className={classNames(
- 'border-bottom py-2 comment-item',
- index === 0 && 'border-top',
- )}>
+ className="border-bottom py-2 comment-item">
{item.showEdit ? (
<Form
className="mt-2"
@@ -397,7 +398,7 @@ const Comment = ({ objectId, mode, commentId }) => {
);
})}
- <div className="mt-2">
+ <div className={classNames(comments.length > 0 && 'py-2')}>
<Button
variant="link"
className="p-0 btn-no-border"
@@ -425,7 +426,10 @@ const Comment = ({ objectId, mode, commentId }) => {
{visibleComment && (
<Form
mode={mode}
- className="mt-2"
+ className={classNames(
+ 'mt-2',
+ comments.length <= 0 && 'bg-light p-3 rounded',
+ )}
onSendReply={(value) => handleSendReply({ value, type: 'comment' })}
onCancel={() => setVisibleComment(!visibleComment)}
/>
diff --git a/ui/src/components/Header/index.tsx
b/ui/src/components/Header/index.tsx
index 9b3b1970..68932962 100644
--- a/ui/src/components/Header/index.tsx
+++ b/ui/src/components/Header/index.tsx
@@ -72,7 +72,7 @@ const Header: FC = () => {
const tagMatch = useMatch('/tags/:slugName');
let askUrl = '/questions/ask';
if (tagMatch && tagMatch.params.slugName) {
- askUrl = `${askUrl}?tags=${tagMatch.params.slugName}`;
+ askUrl = `${askUrl}?tags=${encodeURIComponent(tagMatch.params.slugName)}`;
}
useEffect(() => {
diff --git a/ui/src/components/PluginRender/index.tsx
b/ui/src/components/PluginRender/index.tsx
index 23aebd28..3da9fd0b 100644
--- a/ui/src/components/PluginRender/index.tsx
+++ b/ui/src/components/PluginRender/index.tsx
@@ -48,9 +48,7 @@ const Index: FC<Props> = ({
const pluginSlice: Plugin[] = [];
const plugins = PluginKit.getPlugins().filter((plugin) => plugin.activated);
- console.log('default list', plugins);
plugins.forEach((plugin) => {
- console.log('plugininfo ====', plugin);
if (type && slug_name) {
if (plugin.info.slug_name === slug_name && plugin.info.type === type) {
pluginSlice.push(plugin);
@@ -78,7 +76,6 @@ const Index: FC<Props> = ({
}
if (type === 'editor') {
- console.log('444');
const nodes = React.Children.map(children, (child, index) => {
if (index === 15) {
return (
@@ -86,7 +83,6 @@ const Index: FC<Props> = ({
{child}
{pluginSlice.map((ps) => {
const PluginFC = ps.component;
- console.log('333', ps.info.slug_name);
return (
// @ts-ignore
<PluginFC key={ps.info.slug_name} {...props} />
@@ -98,7 +94,6 @@ const Index: FC<Props> = ({
}
return child;
});
- console.log('222', nodes?.length);
return <div className={className}>{nodes}</div>;
}
diff --git a/ui/src/components/SchemaForm/components/Check.tsx
b/ui/src/components/SchemaForm/components/Check.tsx
index fbb06e9b..3ae1fa4f 100644
--- a/ui/src/components/SchemaForm/components/Check.tsx
+++ b/ui/src/components/SchemaForm/components/Check.tsx
@@ -57,7 +57,6 @@ const Index: FC<Props> = ({
},
};
if (typeof onChange === 'function') {
- console.log('fieldName', fieldName, enumValues);
onChange(state);
}
};
diff --git a/ui/src/components/Tag/index.tsx b/ui/src/components/Tag/index.tsx
index dc151afe..44a5ae63 100644
--- a/ui/src/components/Tag/index.tsx
+++ b/ui/src/components/Tag/index.tsx
@@ -38,7 +38,7 @@ const Index: FC<IProps> = ({
className = '',
textClassName = '',
}) => {
- href ||= pathFactory.tagLanding(data?.slug_name);
+ href ||= pathFactory.tagLanding(encodeURIComponent(data.slug_name));
return (
<Link
diff --git a/ui/src/pages/Admin/Dashboard/components/AnswerLinks/index.tsx
b/ui/src/pages/Admin/Dashboard/components/AnswerLinks/index.tsx
index 19a70fca..89ec601d 100644
--- a/ui/src/pages/Admin/Dashboard/components/AnswerLinks/index.tsx
+++ b/ui/src/pages/Admin/Dashboard/components/AnswerLinks/index.tsx
@@ -36,11 +36,40 @@ const AnswerLinks = () => {
{t('documents')}
</a>
</Col>
+ <Col xs={6}>
+ <a
+ href="https://answer.apache.org/plugins"
+ target="_blank"
+ rel="noreferrer">
+ {t('plugins')}
+ </a>
+ </Col>
<Col xs={6}>
<a href="https://meta.answer.dev" target="_blank" rel="noreferrer">
{t('support')}
</a>
</Col>
+ <Col xs={6}>
+ <a href="https://answer.dev/docs" target="_blank" rel="noreferrer">
+ {t('github')}
+ </a>
+ </Col>
+ <Col xs={6}>
+ <a
+ href="https://answer.apache.org/blog"
+ target="_blank"
+ rel="noreferrer">
+ {t('blog')}
+ </a>
+ </Col>
+ <Col xs={6}>
+ <a
+ href="https://answer.apache.org/contact"
+ target="_blank"
+ rel="noreferrer">
+ {t('contact')}
+ </a>
+ </Col>
</Row>
</Card.Body>
</Card>
diff --git a/ui/src/pages/Admin/Dashboard/components/HealthStatus/index.tsx
b/ui/src/pages/Admin/Dashboard/components/HealthStatus/index.tsx
index 129248ba..c77f0bb1 100644
--- a/ui/src/pages/Admin/Dashboard/components/HealthStatus/index.tsx
+++ b/ui/src/pages/Admin/Dashboard/components/HealthStatus/index.tsx
@@ -76,29 +76,33 @@ const HealthStatus: FC<IProps> = ({ data }) => {
)}
</Col>
<Col xs={6} className="mb-1">
- <span className="text-secondary me-1">{t('https')}</span>
- <strong>{data.https ? t('yes') : t('no')}</strong>
+ <span className="text-secondary me-1">{t('run_mode')}</span>
+ <strong>{data.login_required ? t('private') : t('public')}</strong>
</Col>
<Col xs={6} className="mb-1">
- <span className="text-secondary me-1">{t('uploading_files')}</span>
+ <span className="text-secondary me-1">{t('upload_folder')}</span>
<strong>
- {data.uploading_files ? t('allowed') : t('not_allowed')}
+ {data.uploading_files ? t('writable') : t('not_writable')}
</strong>
</Col>
+ <Col xs={6} className="mb-1">
+ <span className="text-secondary me-1">{t('https')}</span>
+ <strong>{data.https ? t('yes') : t('no')}</strong>
+ </Col>
+ <Col xs={6}>
+ <span className="text-secondary me-1">{t('timezone')}</span>
+ <strong>{data.time_zone.split('/')?.[1]}</strong>
+ </Col>
<Col xs={6}>
<span className="text-secondary me-1">{t('smtp')}</span>
- {data.smtp ? (
- <strong>{t('enabled')}</strong>
+ {data.smtp !== 'not_configured' ? (
+ <strong>{t(data.smtp)}</strong>
) : (
<Link to="/admin/smtp" className="ms-2">
{t('config')}
</Link>
)}
</Col>
- <Col xs={6}>
- <span className="text-secondary me-1">{t('timezone')}</span>
- <strong>{data.time_zone}</strong>
- </Col>
</Row>
</Card.Body>
</Card>
diff --git a/ui/src/pages/Admin/Dashboard/components/Statistics/index.tsx
b/ui/src/pages/Admin/Dashboard/components/Statistics/index.tsx
index 3d293369..85262724 100644
--- a/ui/src/pages/Admin/Dashboard/components/Statistics/index.tsx
+++ b/ui/src/pages/Admin/Dashboard/components/Statistics/index.tsx
@@ -52,7 +52,7 @@ const Statistics: FC<IProps> = ({ data }) => {
<strong>{data.vote_count}</strong>
</Col>
<Col xs={6}>
- <span className="text-secondary me-1">{t('active_users')}</span>
+ <span className="text-secondary me-1">{t('users')}</span>
<strong>{data.user_count}</strong>
</Col>
<Col xs={6}>
diff --git a/ui/src/pages/Admin/Dashboard/components/SystemInfo/index.tsx
b/ui/src/pages/Admin/Dashboard/components/SystemInfo/index.tsx
index 9750fec1..b4314c71 100644
--- a/ui/src/pages/Admin/Dashboard/components/SystemInfo/index.tsx
+++ b/ui/src/pages/Admin/Dashboard/components/SystemInfo/index.tsx
@@ -35,10 +35,22 @@ const SystemInfo: FC<IProps> = ({ data }) => {
<Card.Body>
<h6 className="mb-3">{t('system_info')}</h6>
<Row>
+ <Col xs={6}>
+ <span className="text-secondary me-1">{t('go_version')}</span>
+ <strong>{data.go_version}</strong>
+ </Col>
+ <Col xs={6}>
+ <span className="text-secondary me-1">{t('database')}</span>
+ <strong>{data.database_version}</strong>
+ </Col>
<Col xs={6}>
<span className="text-secondary me-1">{t('storage_used')}</span>
<strong>{data.occupying_storage_space}</strong>
</Col>
+ <Col xs={6}>
+ <span className="text-secondary me-1">{t('database_size')}</span>
+ <strong>{data.database_size}</strong>
+ </Col>
{data.app_start_time ? (
<Col xs={6}>
<span className="text-secondary me-1">{t('uptime')}</span>
diff --git a/ui/src/pages/Admin/Login/index.tsx
b/ui/src/pages/Admin/Login/index.tsx
index ed2c08a7..f2579475 100644
--- a/ui/src/pages/Admin/Login/index.tsx
+++ b/ui/src/pages/Admin/Login/index.tsx
@@ -47,6 +47,12 @@ const Index: FC = () => {
description: t('email_registration.text'),
default: true,
},
+ allow_password_login: {
+ type: 'boolean',
+ title: t('password_login.title'),
+ description: t('password_login.text'),
+ default: true,
+ },
allow_email_domains: {
type: 'string',
title: t('allowed_email_domains.title'),
@@ -73,6 +79,12 @@ const Index: FC = () => {
label: t('email_registration.label'),
},
},
+ allow_password_login: {
+ 'ui:widget': 'switch',
+ 'ui:options': {
+ label: t('password_login.label'),
+ },
+ },
allow_email_domains: {
'ui:widget': 'textarea',
},
@@ -105,6 +117,7 @@ const Index: FC = () => {
allow_email_registrations: formData.allow_email_registrations.value,
allow_email_domains: allowedEmailDomains,
login_required: formData.login_required.value,
+ allow_password_login: formData.allow_password_login.value,
};
putLoginSetting(reqParams)
diff --git a/ui/src/pages/Questions/Ask/index.tsx
b/ui/src/pages/Questions/Ask/index.tsx
index 49f462f5..949b1d67 100644
--- a/ui/src/pages/Questions/Ask/index.tsx
+++ b/ui/src/pages/Questions/Ask/index.tsx
@@ -17,14 +17,14 @@
* under the License.
*/
-import React, { useState, useEffect, useRef } from 'react';
+import React, { useState, useEffect, useRef, useCallback } from 'react';
import { Row, Col, Form, Button, Card } from 'react-bootstrap';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';
import classNames from 'classnames';
-import { isEqual } from 'lodash';
+import { isEqual, debounce } from 'lodash';
import { usePageTags, usePromptWithUnload, useCaptchaModal } from '@/hooks';
import { Editor, EditorRef, TagSelector } from '@/components';
@@ -35,7 +35,7 @@ import {
questionDetail,
modifyQuestion,
useQueryRevisions,
- useQueryQuestionByTitle,
+ queryQuestionByTitle,
getTagsBySlugName,
saveQuestionWithAnswer,
} from '@/services';
@@ -94,6 +94,7 @@ const Ask = () => {
setCheckState(false);
setForceType('');
};
+ const [similarQuestions, setSimilarQuestions] = useState([]);
const editorRef = useRef<EditorRef>({
getHtml: () => '',
@@ -117,9 +118,6 @@ const Ask = () => {
};
const isEdit = qid !== undefined;
- const { data: similarQuestions = { list: [] } } = useQueryQuestionByTitle(
- isEdit ? '' : formData.title.value,
- );
const saveCaptcha = useCaptchaModal('question');
const editCaptcha = useCaptchaModal('edit');
@@ -221,11 +219,23 @@ const Ask = () => {
});
}, [qid]);
+ const querySimilarQuestions = useCallback(
+ debounce((title) => {
+ queryQuestionByTitle(title).then((res) => {
+ setSimilarQuestions(res);
+ });
+ }, 400),
+ [],
+ );
+
const handleTitleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFormData({
...formData,
title: { ...formData.title, value: e.currentTarget.value, errorMsg: '' },
});
+ if (e.currentTarget.value.length >= 10) {
+ querySimilarQuestions(e.currentTarget.value);
+ }
};
const handleContentChange = (value: string) => {
setFormData({
diff --git a/ui/src/pages/Tags/Create/index.tsx
b/ui/src/pages/Tags/Create/index.tsx
index bf41fd58..485c7cd6 100644
--- a/ui/src/pages/Tags/Create/index.tsx
+++ b/ui/src/pages/Tags/Create/index.tsx
@@ -115,7 +115,7 @@ const Index = () => {
};
createTag(params)
.then((res) => {
- navigate(`/tags/${res.slug_name}/info`, {
+ navigate(`/tags/${encodeURIComponent(res.slug_name)}/info`, {
replace: true,
});
})
diff --git a/ui/src/pages/Tags/Edit/index.tsx b/ui/src/pages/Tags/Edit/index.tsx
index 32ce788d..a01e278c 100644
--- a/ui/src/pages/Tags/Edit/index.tsx
+++ b/ui/src/pages/Tags/Edit/index.tsx
@@ -162,7 +162,7 @@ const Index = () => {
edit_summary: formData.editSummary.value,
};
modifyTag(params).then((res) => {
- navigate(`/tags/${formData.slugName.value}/info`, {
+ navigate(`/tags/${encodeURIComponent(formData.slugName.value)}/info`, {
replace: true,
state: { isReview: res.wait_for_review },
});
diff --git a/ui/src/pages/Timeline/index.tsx b/ui/src/pages/Timeline/index.tsx
index cd283616..e20d50ef 100644
--- a/ui/src/pages/Timeline/index.tsx
+++ b/ui/src/pages/Timeline/index.tsx
@@ -86,8 +86,9 @@ const Index: FC = () => {
if (timelineData?.object_info.object_type === 'tag') {
linkUrl = `/tags/${
- timelineData?.object_info.main_tag_slug_name ||
- timelineData?.object_info.title
+ timelineData?.object_info.main_tag_slug_name
+ ? encodeURIComponent(timelineData?.object_info.main_tag_slug_name)
+ : encodeURIComponent(timelineData?.object_info.title)
}`;
pageTitle = `${t('title_for_tag')} '${timelineData?.object_info.title}'`;
}
@@ -97,6 +98,8 @@ const Index: FC = () => {
usePageTags({
title: pageTitle,
});
+
+ console.log('timelineData', linkUrl);
return (
<div className="py-4 mb-5">
<h5 className="mb-4">
diff --git a/ui/src/pages/Users/Login/index.tsx
b/ui/src/pages/Users/Login/index.tsx
index 518f5b38..549808bd 100644
--- a/ui/src/pages/Users/Login/index.tsx
+++ b/ui/src/pages/Users/Login/index.tsx
@@ -45,7 +45,8 @@ const Index: React.FC = () => {
ucAgentInfo = ucAgent.agent_info;
}
const canOriginalLogin =
- !ucAgentInfo || ucAgentInfo.enabled_original_user_system;
+ (!ucAgentInfo || ucAgentInfo.enabled_original_user_system) &&
+ loginSetting.allow_password_login;
const [formData, setFormData] = useState<FormDataType>({
e_mail: {
diff --git a/ui/src/router/pathFactory.ts b/ui/src/router/pathFactory.ts
index 97d74afb..ec326acf 100644
--- a/ui/src/router/pathFactory.ts
+++ b/ui/src/router/pathFactory.ts
@@ -20,12 +20,12 @@
import { seoSettingStore } from '@/stores';
const tagLanding = (slugName: string) => {
- const r = slugName ? `/tags/${slugName}` : '/tags';
+ const r = slugName ? `/tags/${encodeURIComponent(slugName)}` : '/tags';
return r;
};
const tagInfo = (slugName: string) => {
- const r = slugName ? `/tags/${slugName}/info` : '/tags';
+ const r = slugName ? `/tags/${encodeURIComponent(slugName)}/info` : '/tags';
return r;
};
diff --git a/ui/src/services/common.ts b/ui/src/services/common.ts
index f4efca73..e222ba93 100644
--- a/ui/src/services/common.ts
+++ b/ui/src/services/common.ts
@@ -30,11 +30,8 @@ export const uploadImage = (params: { file: File; type:
Type.UploadType }) => {
return request.post('/answer/api/v1/file', form);
};
-export const useQueryQuestionByTitle = (title) => {
- return useSWR<Record<string, any>>(
- title ? `/answer/api/v1/question/similar?title=${title}` : '',
- request.instance.get,
- );
+export const queryQuestionByTitle = (title: string) => {
+ return request.get(`/answer/api/v1/question/similar?title=${title}`);
};
export const useQueryTags = (params) => {
diff --git a/ui/src/stores/loginSetting.ts b/ui/src/stores/loginSetting.ts
index c692915d..15d572f1 100644
--- a/ui/src/stores/loginSetting.ts
+++ b/ui/src/stores/loginSetting.ts
@@ -32,6 +32,7 @@ const loginSetting = create<IType>((set) => ({
login_required: false,
allow_email_registrations: true,
allow_email_domains: [],
+ allow_password_login: true,
},
update: (params) =>
set(() => {
diff --git a/ui/src/utils/request.ts b/ui/src/utils/request.ts
index c2c4b587..31a22019 100644
--- a/ui/src/utils/request.ts
+++ b/ui/src/utils/request.ts
@@ -136,8 +136,7 @@ class Request {
// default error msg will show modal
Modal.confirm({
content: msg,
- showCancel: false,
- confirmText: 'OK',
+ showConfirm: false,
});
return Promise.reject(false);
}